bzoj 1499 [NOI2005]瑰麗華爾茲 (單調隊列優化DP)

題目大意:給你一個n*m棋盤(n,m<=200),有一個人從給定的點s,e出發,有一些壞點不能走,一共給定k段連續的時間(k<=200),在某一段時間之內它只能向一個給定的方向移動,在某一時刻,它可以移動或者不移動。求碰到壞點之前/總時間結束時,最長移動的距離。

樸素DP的方法是:  f[t][i][j] 表示在時間點t時,在位置(i,j)時已經走過的最長距離,i' j'表示這一步移動前的位置

得到方程f[t][i][j]=max(f[t][i][j],f[t-1][i'][j']+1)

空間時間都會炸

那麼重新定義 f[k][i][j] 表示在第k個時間段,在位置(i,j)時已經走過的最長距離

那麼顯然f[k][i][j]=max(f[k-1][i'][j']+dis<(i',j'),(i,j)>),注意要根據移動的方向決定枚舉的順序

由於dis在同一行/列具有單調性,用隊列隨便優化一下就行了,如果dis大於t[i]-s[i]就刪除隊首,如果碰到障礙就清隊列

#include <cstdio>
#include <algorithm>
#include <cstring>
#define N 52
using namespace std;

int n,m,sx,sy,K,now,pst;
int mp[N][N],st[N],ed[N],dir[N],que[N];
int xx[]={0,-1,1,0,0},yy[]={0,0,0,-1,1};
char str[N][N];
int f[2][N][N];
void upd1(int k)
{
    int hd,tl;
    for(int j=1;j<=m;j++)
    {
        hd=1,tl=0;
        for(int i=n;i>=1;i--)
        {
            if(mp[i][j]) {hd=1,tl=0;continue;}
            while(hd<=tl&&f[pst][que[tl]][j]+que[tl]-i<=f[pst][i][j])
                tl--;
            que[++tl]=i;
            while(hd<=tl&&que[hd]-i>ed[k]-st[k]+1)
                hd++;
            f[now][i][j]=f[pst][que[hd]][j]+que[hd]-i;
        }
    }
}
void upd2(int k)
{
    int hd,tl;
    for(int j=1;j<=m;j++)
    {
        hd=1,tl=0;
        for(int i=1;i<=n;i++)
        {
            if(mp[i][j]) {hd=1,tl=0;continue;}
            while(hd<=tl&&f[pst][que[tl]][j]+i-que[tl]<=f[pst][i][j])
                tl--;
            que[++tl]=i;
            while(hd<=tl&&i-que[hd]>ed[k]-st[k]+1)
                hd++;
            f[now][i][j]=f[pst][que[hd]][j]+i-que[hd];
        }
    }
}
void upd3(int k)
{
    int hd,tl;
    for(int i=1;i<=n;i++)
    {
        hd=1,tl=0;
        for(int j=m;j>=1;j--)
        {
            if(mp[i][j]) {hd=1,tl=0;continue;}
            while(hd<=tl&&f[pst][i][que[tl]]+que[tl]-j<=f[pst][i][j])
                tl--;
            que[++tl]=j;
            while(hd<=tl&&que[hd]-j>ed[k]-st[k]+1)
                hd++;
            f[now][i][j]=f[pst][i][que[hd]]+que[hd]-j;
        }
    }
}
void upd4(int k)
{
    int hd,tl;
    for(int i=1;i<=n;i++)
    {
        hd=1,tl=0;
        for(int j=1;j<=m;j++)
        {
            if(mp[i][j]) {hd=1,tl=0;continue;}
            while(hd<=tl&&f[pst][i][que[tl]]+j-que[tl]<=f[pst][i][j])
                tl--;
            que[++tl]=j;
            while(hd<=tl&&j-que[hd]>ed[k]-st[k]+1)
                hd++;
            f[now][i][j]=f[pst][i][que[hd]]+j-que[hd];
        }
    }
}

int main()
{
    scanf("%d%d%d%d%d",&n,&m,&sx,&sy,&K);
    for(int i=1;i<=n;i++){
        scanf("%s",str[i]+1);
        for(int j=1;j<=m;j++)
            if(str[i][j]=='x')
                mp[i][j]=1;
    }
    for(int i=1;i<=K;i++)
        scanf("%d%d%d",&st[i],&ed[i],&dir[i]);
    memset(f,-0x3f,sizeof(f));
    f[0][sx][sy]=0;
    now=1,pst=0;
    for(int k=1;k<=K;k++)
    {
        if(dir[k]==1) upd1(k);
        if(dir[k]==2) upd2(k);
        if(dir[k]==3) upd3(k);
        if(dir[k]==4) upd4(k);
        swap(now,pst);
    }
    int ans=0;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
        ans=max(f[pst][i][j],ans);
    printf("%d\n",ans);
    return 0;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章