bzoj1499 [NOI2005]瑰丽华尔兹——单调队列优化DP

题目:https://www.lydsy.com/JudgeOnline/problem.php?id=1499

朴素DP方程很好想,以右移为例,就是 f[i][x][y]=max(f[i][x][y],f[i-1][x][j]+y-j) ;

每一行/列会用到一些相同的状态更新,所以可以用单调队列优化。

代码如下:

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int const maxn=205,inf=0x3f3f3f3f;
int n,m,K,st,ed,f[3][maxn][maxn],xx[5]={0,-1,1,0,0},yy[5]={0,0,0,-1,1},L,ans;
char s[maxn][maxn];
struct N{
    int pos,ans;
}q[maxn];
void dp(int x,int y,int d,int nw)
{
    int h=1,t=0;N tmp;
    for(int i=0;x&&y&&x<=n&&y<=m;i++,x+=xx[d],y+=yy[d])//i为与原来的y的差距,注意不要与y搞混 
    {
        if(s[x][y]=='x')h=1,t=0;//h!=0
        tmp.pos=i;tmp.ans=f[nw^1][x][y];
        while(h<=t&&i-q[h].pos>L)h++;//i  //<=
        while(h<=t&&tmp.ans>=q[t].ans+i-q[t].pos)t--;//i
        q[++t]=tmp;
        if(h>t)f[nw][x][y]=-inf;//
        else f[nw][x][y]=q[h].ans+i-q[h].pos;
        ans=max(ans,f[nw][x][y]);
    }
}
int main()
{
    scanf("%d%d%d%d%d",&n,&m,&st,&ed,&K);
    for(int i=1;i<=n;i++)cin>>s[i]+1;
    memset(f,-0x3f,sizeof f);
    f[0][st][ed]=0;
    int l,r,d,nw=0;
    while(K--)
    {
        nw^=1;
        scanf("%d%d%d",&l,&r,&d);
        L=r-l+1;
        if(d==1)for(int i=1;i<=m;i++)dp(n,i,d,nw);
        if(d==2)for(int i=1;i<=m;i++)dp(1,i,d,nw);
        if(d==3)for(int j=1;j<=n;j++)dp(j,m,d,nw);
        if(d==4)for(int j=1;j<=n;j++)dp(j,1,d,nw);
    }
    printf("%d",ans);
    return 0;
}

 

posted @ 2018-06-10 15:58  Zinn  阅读(157)  评论(0编辑  收藏  举报