【bzoj1499】[NOI2005]瑰丽华尔兹 【单调队列优化dp】

题目链接
题解:
首先,我们很容易得出dp方程。让f[i][j][k]表示在第k段时间里走到(i,j)这个位置的最长滑动距离。这里以向北滑动举例,设第段时间内最多能走l步,l=s[k]t[k]+1。则有f[i][j][k]=max(f[a][j][k1]+aj)(aj,alj)。于是我们就可以用一个单调队列维护f[a][j][k1]+a的最大值。
其他四个方向同理,就是循环的顺序变一下而已。
k这一维内存可以用滚动数组优化掉。注意把到不了的位置赋值为-inf,只有队头的最优值不为-inf才更新当前的位置。遇到障碍物,要把队列清空,因为之前的决策全部没用了,走不到当前位置。初始化时把除初始位置之外的都赋值为-inf。
细节详见代码。

#include<cstdio>
const int N=205,inf=0x3f3f3f3f;
int n,m,x,y,k,s,t,d,ans,head,tail,q[N],f[N][N],g[N][N];
char str[N][N];
void dp(int l){
    if(d==1){
        for(int j=1;j<=m;j++){
            head=1,tail=0;
            for(int i=n;i>=1;i--){
                if(str[i][j]=='x'){
                    head=1,tail=0;
                    continue;
                }
                while(head<=tail&&q[head]-i>l){
                    head++;
                }
                while(head<=tail&&g[i][j]+i>g[q[tail]][j]+q[tail]){
                    tail--;
                }
                q[++tail]=i;
                if(g[q[head]][j]!=-inf){
                    f[i][j]=g[q[head]][j]+q[head]-i;
                }
            }
        }
    }else if(d==2){
        for(int j=1;j<=m;j++){
            head=1,tail=0;
            for(int i=1;i<=n;i++){
                if(str[i][j]=='x'){
                    head=1,tail=0;
                    continue;
                }
                while(head<=tail&&i-q[head]>l){
                    head++;
                }
                while(head<=tail&&g[i][j]-i>g[q[tail]][j]-q[tail]){
                    tail--;
                }
                q[++tail]=i;
                if(g[q[head]][j]!=-inf){
                    f[i][j]=g[q[head]][j]+i-q[head];
                }
            }
        }
    }else if(d==3){
        for(int i=1;i<=n;i++){
            head=1,tail=0;
            for(int j=m;j>=1;j--){
                if(str[i][j]=='x'){
                    head=1,tail=0;
                    continue;
                }
                while(head<=tail&&q[head]-j>l){
                    head++;
                }
                while(head<=tail&&g[i][j]+j>g[i][q[tail]]+q[tail]){
                    tail--;
                }
                q[++tail]=j;
                if(g[i][q[head]]!=-inf){
                    f[i][j]=g[i][q[head]]+q[head]-j;
                }
            }
        }
    }else{
        for(int i=1;i<=n;i++){
            head=1,tail=0;
            for(int j=1;j<=m;j++){
                if(str[i][j]=='x'){
                    head=1,tail=0;
                    continue;
                }
                while(head<=tail&&j-q[head]>l){
                    head++;
                }
                while(head<=tail&&g[i][j]-j>g[i][q[tail]]-q[tail]){
                    tail--;
                }
                q[++tail]=j;
                if(g[i][q[head]]!=-inf){
                    f[i][j]=g[i][q[head]]+j-q[head];
                }
            }
        }
    }
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            g[i][j]=f[i][j];
            if(f[i][j]>ans){
                ans=f[i][j];
            }
        }
    }
}
int main(){
    scanf("%d%d%d%d%d",&n,&m,&x,&y,&k);
    for(int i=1;i<=n;i++){
        scanf("%s",str[i]+1);
        for(int j=1;j<=m;j++){
            f[i][j]=g[i][j]=-inf;
        }
    }
    f[x][y]=g[x][y]=0;
    for(int i=1;i<=k;i++){
        scanf("%d%d%d",&s,&t,&d);
        dp(t-s+1);
    }
    printf("%d\n",ans);
    return 0;
}
posted @ 2018-03-31 20:22  ez_2016gdgzoi471  阅读(154)  评论(0编辑  收藏  举报