瑰丽华尔兹

【题目描述】

舞厅是一个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值可以随纵纵横横的处理转移到其他任意的点,所以正确。
*/

 

posted @ 2016-07-26 14:46  前前前世。  阅读(256)  评论(0编辑  收藏  举报