標籤:
340. [NOI2005] 瑰麗華爾茲
★★★ 輸入檔案:adv1900.in 輸出檔案:adv1900.out 簡單對比時間限制:1 s 記憶體限制:128 MB
【任務描述】
你跳過華爾茲嗎?當音樂響起,當你隨著旋律滑動舞步,是不是有一種漫步仙境的愜意?眾所周知,跳華爾茲時,最重要的是有好的音樂。但是很少有幾個人知道,世界上最偉大的鋼琴家一生都漂泊在大海上,他的名字叫丹尼?布德曼?T.D.?檸檬?1900,朋友們都叫他1900。
1900出生於20世紀的第一年出生在往返於歐美的郵輪維吉尼亞號上,然後就被拋棄了。1900剛出生就成了孤兒,孤獨的成長在維吉尼亞號上,從未離開過這個搖晃的世界;也許是對他命運的補償,上帝派可愛的小天使艾米麗照顧他。
可能是天使的點化,1900擁有不可思議的鋼琴天賦,從未有人教,從沒看過樂譜,但他卻能憑著自己的感覺彈出最沁人心脾的旋律。當1900的音樂獲得郵輪上所有人的歡迎時,他才8歲,而此時他已經乘著海輪往返歐美50多次了。
雖說是鋼琴奇才,但1900還是個8歲的孩子,他有著和一般男孩一樣的好奇的調皮,不過可能更有一層浪漫的色彩罷了:
這是一個風雨交加的夜晚,海風捲起層層巨浪拍打著維吉尼亞號,郵輪隨著巨浪劇烈的搖擺。船上的新薩克斯手邁克斯?托尼暈船了,1900將他 邀請到舞廳,然後——,然後鬆開了固定鋼琴的閘,於是,鋼琴隨著海輪的傾斜滑動起來。準確的說,我們的主角1900、鋼琴、郵輪隨著1900的旋律一起跳 起了華爾茲,所有的事物好像都化為一體,隨著“強弱弱”的節奏,托尼的暈船症也奇蹟般地一點一點恢複。正如托尼在回憶錄上這樣寫道:
大海搖晃著我們
使我們轉來轉去
快速的掠過燈和傢具
我意識到我們正在和大海一起跳舞
真是完美而瘋狂的舞者
晚上在金色的地板上快樂的跳著華爾茲是不是很愜意呢?也許,我們忘記了一個人,那就是艾米麗,她可沒閑著:她必須在適當的時候施魔法協助1900,不讓鋼琴碰上舞廳裡的傢具。而艾米麗還小,她無法施展魔法改變鋼琴的運動方向或速度,而只能讓鋼琴停一下。
不妨認為舞廳是一個N行M列的矩陣,矩陣中的某些方格上堆放了一些傢具,其他的則是空地。鋼琴可以在空地上滑動,但不能撞上傢具或滑出舞廳,否則會損壞鋼琴和傢具,引來難纏的船長。
每個時刻,鋼琴都會隨著船體傾斜的方向向相鄰的方格滑動一格,其中相鄰的方格可以是向東、向西、向南或向北的。而艾米麗可以選擇施魔法或不施魔法,如果不施魔法,則鋼琴會滑動,而如果施魔法,則鋼琴會原地不動。 艾米麗是個天使,她知道每段時間的船體的傾斜情況。她想使鋼琴盡量長時間在舞廳裡滑行,這樣1900會非常高興,同時也有利於治療托尼的暈船。但艾米麗還太小,不會算,所以希望你能協助她。
【輸入格式】
輸入檔案的第一行包含5個數N, M, x, y和K。N和M描述舞廳的大小,x和y為在第1時刻初鋼琴的位置(x行y列);我們對船體傾斜情況是按時間的區間來描述的,比如“在[1, 3]時間裡向東傾斜,[4, 5]時間裡向北傾斜”,因此這裡的K表示區間的數目。
以下N行,每行M個字元,描述舞廳裡的傢具。第i行第j列的字元若為‘ . ‘,則表示該位置是空地;若為‘ x ‘,則表示有傢具。
以下K行,順序描述K個時間區間,格式為:si ti di(1 ≤ i ≤ K)。表示在時間區間[si, ti]內,船體都是向di方向傾斜的。di為1, 2, 3, 4中的一個,依次表示北、南、西、東(分別對應矩陣中的上、下、左、右)。輸入保證區間是連續的,即
s1 = 1ti = si-1 + 1 (1 < i ≤ K)tK = T
【輸出格式】
輸出檔案僅有1行,包含一個整數,表示鋼琴滑行的最長距離(即格子數)。
【輸入範例】
4 5 4 1 3..xx.........x......1 3 44 5 16 7 3
【輸出範例】
6
【範例說明】
鋼琴的滑行路線:
鋼琴在“×位置上時天使使用一次魔法,因此滑動總長度為6。
【評分方法】
本題沒有部分分,你的程式的輸出只有和我們的答案完全一致才能獲得滿分,否則不得分。
【資料範圍】
50%的資料中,1≤N, M≤200,T≤200;
100%的資料中,1≤N, M≤200,K≤200,T≤40000。
題解:
第一次見題目描述如此長且沒太多用處的題目。。
首先考慮對於時間t來dp:
f[t][i][j]表示在第t時刻在第i行第j列所能獲得的最長距離。
轉移方程顯然:
f[t][i][j]=max(f[t-1][i][j],f[t][i*][j*]+1)(i*,j*為上一個合理的位置)
這樣時間複雜度為O(TNM),可以過50%,但對於100%逾時。
所以考慮一下另一種dp方式:
f[k][i][j]表示在第k段滑行區間中在第i行第j列獲得最長距離。
考慮轉移:
對於d==1(即向上走),可以發現每一列的資訊是可以轉移的,同時,每一列的資訊也是可以通過單調隊列維護的資訊十分快速的求出,可以腦補,複雜度為O(KNM)。
Code:
#include<iostream>#include<cstdio>#include<cstdlib>#include<cstring>#include<cmath>#include<algorithm>#define N 210#define inf -2139062144using namespace std;int n,m,X,Y,K,T,h,t,ans=0;int q[100*N],Q[100*N],f[N][N][N],dx[6]={0,-1,1,0,0},dy[6]={0,0,0,-1,1};bool a[N][N];int in(){ int x=0; char ch=getchar(); while (ch<‘0‘ || ch>‘9‘) ch=getchar(); while (ch>=‘0‘ && ch<=‘9‘) x=x*10+ch-‘0‘,ch=getchar(); return x;}void push(int k,int now,int x,int y){ if (k==inf) return; while (h<=t && (k-now)>Q[t]) t--; Q[++t]=k-now; q[t]=now;}void dp(int xu,int x,int y,int d){ int now=1; h=1,t=0; while (1){ if (x<1 || x>n || y<1 || y>m) break; if (!a[x][y]) h=1,t=0; else push(f[xu-1][x][y],now,x,y); while (h<=t && (now-q[h])>T) h++; if (h<=t) f[xu][x][y]=Q[h]+now; else f[xu][x][y]=inf; ans=max(ans,f[xu][x][y]); x+=dx[d],y+=dy[d]; now++; }}void work(int xu,int s,int t,int d){ T=t-s+1; if (T<1) return; if (d==1) for (int i=1; i<=m; i++) dp(xu,n,i,d); else if (d==2) for (int i=1; i<=m; i++) dp(xu,1,i,d); else if (d==3) for (int i=1; i<=n; i++) dp(xu,i,m,d); else for (int i=1; i<=n; i++) dp(xu,i,1,d);}int main(){ n=in(),m=in(),X=in(),Y=in(),K=in(); memset(f,128,sizeof(f)); f[0][X][Y]=0; for (int i=1; i<=n; i++) for (int j=1; j<=m; j++){ char ch=getchar(); while (ch!=‘.‘ && ch!=‘x‘) ch=getchar(); if (ch==‘.‘) a[i][j]=1; else a[i][j]=0; } for (int i=1; i<=K; i++){ int s=in(),t=in(),d=in(); work(i,s,t,d); } printf("%d\n",ans); return 0;}
著作權聲明:本文為博主原創文章,未經博主允許不得轉載。
【DP】【單調隊列】【NOI2005】瑰麗華爾茲