Live2d Test Env

BZOJ1499: 瑰丽华尔兹(单调队列)

pro: 给出一个n*m的地图,刚开始人在(x,y),每次给出一段区间(l,r,t),表示在时间[l,r]内,可以使人向4个方向(t)移动一格,或者不动。求最大可以移动多少格。

sol: 考虑每一列(上下移)或者一行(左右移)的情况。以右移为例,我们可以很快列出dp方程:f[x][y][i]=max(f[x][y][i],f[x][j][i]+y-j)。这个dp方程我们可以用单调队列维护,所以复杂度就是nmk的。

#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=b;i++)
using namespace std;
const int maxn=210;
const int inf=1e9+7;
int dp[maxn][maxn][2],ans;
char c[maxn][maxn];
int X[5]={0,-1,1,0,0},Y[5]={0,0,0,-1,1};
int q[maxn][3],head,tail,N,M,now;
int update(int x,int y,int tp,int tk)
{
    int res=dp[q[tp][0]][q[tp][1]][(tk^1)&1]+now-q[tp][2];
    dp[x][y][tk&1]=max(dp[x][y][tk&1],res);
    return res;
}
int get(int tp,int tk)
{
    int res=dp[q[tp][0]][q[tp][1]][(tk^1)&1]+now-q[tp][2];
    return res;
}
void work(int x,int y,int tK,int len,int opt)
{
    head=1,tail=0; now=0;
    while(x>=1&&x<=N&&y>=1&&y<=M) {
        dp[x][y][tK&1]=dp[x][y][(tK^1)&1];
        if(c[x][y]=='x') head=1,tail=0;
        while(tail>=head&&now-q[head][2]>len) head++;
        while(tail>=head&&get(tail,tK)<dp[x][y][(tK^1)&1]) tail--;
        if(tail>=head)
            update(x,y,head,tK);
        ans=max(ans,dp[x][y][tK&1]);
        tail++; q[tail][0]=x; q[tail][1]=y; q[tail][2]=now++;
        x+=X[opt]; y+=Y[opt];
    }
}
int main()
{
    int K,x,y,L,R,opt;
    scanf("%d%d%d%d%d",&N,&M,&x,&y,&K);
    rep(i,1,N) scanf("%s",c[i]+1);
    rep(i,1,N) rep(j,1,M) rep(k,0,K) dp[i][j][k]=-inf;
    dp[x][y][0]=0;
    rep(i,1,K){
        scanf("%d%d%d",&L,&R,&opt);
        if(opt==1) rep(j,1,M) work(N,j,i,R-L+1,opt);
        if(opt==2) rep(j,1,M) work(1,j,i,R-L+1,opt);
        if(opt==3) rep(j,1,N) work(j,M,i,R-L+1,opt);
        if(opt==4) rep(j,1,N) work(j,1,i,R-L+1,opt);
    }
    printf("%d\n",ans);
    return 0;
}

 

posted @ 2019-06-07 16:16  nimphy  阅读(190)  评论(0编辑  收藏  举报