瑰丽华尔兹
【题目描述】
舞厅是一个N行M列的矩阵,矩阵中的某些方格上堆放了一些家具,其他的则是空地。钢琴可以在空地上滑动,但不能撞上家具或滑出舞厅。每个时刻,钢琴都会随着船体倾斜的方向向相邻的方格滑动一格,相邻的方格可以是向东、向西、向南或向北的。而艾米丽可以选择施魔法或不施魔法:如果不施魔法,则钢琴会滑动;如果施魔法,则钢琴会原地不动。艾米丽知道每段时间的船体的倾斜情况。她想使钢琴在舞厅里滑行的路程尽量长,希望你能帮助她。
【输入描述】
第一行包含5个数:N、M、x、y和K。N和M描述舞厅的大小,x和y为钢琴的初始位置,我们对船体倾斜情况是按时间的区间来描述的,且从1开始计算时间,比如“在[1,3]时间里向东倾斜,[4,5]时间里向北倾斜”,因此这里的K表示区间的数目。以下N行,每行M个字符,描述舞厅里的家具。第i行第j列的字符若为‘.’,则表示该位置是空地;若为‘x’,则表示有家具。以下K行,顺序描述K个时间区间,格式为:s[i]、t[i]、d[i](1 ≤ i ≤ K)。表示在时间区间[s[i],t[i]]内,船体都是向d[i]方向倾斜的。d[i]为1、2、3、4中的一个,依次表示北、南、西、东(分别对应矩阵中的上、下、左、右)。输入保证区间是连续的,即:
s[1]=1
t[i]=s[i-1]+1(1 < i ≤ K)
t[K]=T
【输出描述】
输出文件仅有1行,包含一个整数,表示钢琴滑行的最长距离(即格子数)。
【样例输入】
4 5 4 1 3
..xx.
.....
...x.
.....
1 3 4
4 5 1
6 7 2
【样例输出】
6
【数据范围及提示】
50%的数据中,1 ≤ N,M ≤ 200,T ≤ 200;
100%的数据中,1 ≤ N,M≤ 200,K ≤ 200,T ≤ 40000。
源代码: #include<cstdio> #include<algorithm> #define INF 1000000000 using namespace std; char S[201][201]; int DP[201][201][201]; int x[4]={-1,1,0,0},y[4]={0,0,-1,1}; //由于要对应方向,所以是有顺序的。 int Q[201],Pos[201]; int N,M,X,Y,K,Head,Tail,ans; void Push(int Now,int T,int T1,int T2) { if (T==-INF) //这个点连到都没到过。 return; while (T-Now>Q[Tail]&&Head<=Tail) //入队前先删除队尾,队尾不够优,就删除。即新入队的元素的可更新距离大于队尾的。 Tail--; Q[++Tail]=T-Now; Pos[Tail]=Now; } void Solve(int Num,int T1,int T2,int D,int T) //Num表示当前区间编号,T表示此区间内最多能移动的长度。 { Head=1; Tail=0; int Now=1; while (T1<=N&&T1>=1&&T2<=M&&T2>=1) { if (S[T1][T2]=='x') //一旦有了障碍物,之前的点都用不到了。 { Head=1; Tail=0; } else Push(Now,DP[Num-1][T1][T2],T1,T2); //尝试入队。 while (Now-Pos[Head]>T&&Head<=Tail) Head++; //利用区间长度删除过长的队头,队头储存着这一列(行)距离最远的点。 if (Head<=Tail) //更新DP数组。 DP[Num][T1][T2]=Q[Head]+Now; else DP[Num][T1][T2]=-INF; ans=max(ans,DP[Num][T1][T2]); T1+=x[D]; T2+=y[D]; //按顺序,沿着此列(行)更新下一点。 Now++; //更新距离长度。 } } int main() { scanf("%d%d%d%d%d",&N,&M,&X,&Y,&K); for (int a=1;a<=N;a++) //怎么都用字符串读入。 scanf("%s",S[a]+1); for (int a=1;a<=N;a++) for (int b=1;b<=M;b++) DP[0][a][b]=-INF; //赋最小值。 DP[0][X][Y]=0; for (int a=1;a<=K;a++) { int T1,T2,T; scanf("%d%d%d",&T1,&T2,&T); if (T==1) //上。 for (int b=1;b<=M;b++) Solve(a,N,b,T-1,T2-T1+1); if (T==2) //下。 for (int b=1;b<=M;b++) Solve(a,1,b,T-1,T2-T1+1); if (T==3) //左。 for (int b=1;b<=N;b++) Solve(a,b,M,T-1,T2-T1+1); if (T==4) //右。 for (int b=1;b<=N;b++) Solve(a,b,1,T-1,T2-T1+1); } printf("%d",ans); return 0; } /* 设DP[k][i][j]表示第k个区间时,(X,Y)到达(i,j)的最多步数,L为区间长度也为此区间内最多移动步数。 状态转移方程(以向下为例): DP[k][i][j]=max(DP[k-1][T][j])+(i-T),(i-L <= T <= i) 一列(行)一列(行)地进行DP,则如果中途有障碍物,那么之前便无用了。 一个点的DP值可以随纵纵横横的处理转移到其他任意的点,所以正确。 */