洛谷2254 BZOJ1499 瑰丽华尔兹题解

洛谷链接

BZ链接

一个很容易想到的做法就是用f[i][j][t]表示t时刻在i,j处的可以滑动的最大值

f[i][j][t]=max(f[i][j][t-1],f[*i][*j][t-1]),这样大力转移

只不过会TLE+MLE

所以我们要进行一下优化

f[i][j][k]表示在第k个时间段在i,j处的可以滑动的最大值

f[i][j][k]=max(f[*i][*j][k-1]+dis(i,j,*i,*j,f[i][j][k-1])

//*i,*j表示上一个合理的位置

注意到我们的i,与*i,以及j与*j一定有一个相等,即它们在同一行或者同一列

所以我们可以根据滑动的路径一行一行或者一列一列进行转移

而这个位置的可以转移来的位置即是它向前len长度的之内的位置

这与滑动窗口很类似,可以用单调队列来维护max(f[*i][*j][k-1])

然后在考虑障碍,我们发现一旦遇到障碍,之前的答案都不能转移过来

所以在有障碍是清空队列就好了

还有一个常规的优化,就是f[i][j][k]仅与f[i][j][k-1]有关,所以可以利用滚动数组进行优化

# include<iostream>
# include<cstdio>
# include<cmath>
# include<cstring>
# include<algorithm>
using namespace std;
const int mn = 205;
const int dx[]={0,-1,1,0,0};
const int dy[]={0,0,0,-1,1};
struct node{int val,pos;};
node q[mn];
node make_node(int x,int y)
{
    node tmp;tmp.val=x,tmp.pos=y;
    return tmp;
}
int n,m,sx,sy,k;
int dp[mn][mn],ans,d;
char s[mn];
bool vis[mn][mn];
void work(int x,int y,int len)
{
    int tail=1,head=0;
    for(int i=1;x>=1 && x<=n && y>=1 && y<=m;i++,x+=dx[d],y+=dy[d])
    {
        if(!vis[x][y]) tail=1,head=0;
        else {
           while(tail<=head && q[head].val+i-q[head].pos<=dp[x][y]) head--;
           q[++head] = make_node(dp[x][y],i);//dp[x][y]实际上是dx[x][y][k-1]
       //BZOJ不支持c++11,写成q[++head]=node{dp[x][y],i}会CE
while(tail<=head && q[head].pos-q[tail].pos>len) tail++; dp[x][y] = q[tail].val+i-q[tail].pos;//现在dp[x][y]才是dp[x][y][k] ans=max(ans,dp[x][y]); } } } int main() { int x,y,z; scanf("%d%d%d%d%d",&n,&m,&sx,&sy,&k); for(int i=1;i<=n;i++) { scanf("%s",s+1); for(int j=1;j<=m;j++) if(s[j]=='.') vis[i][j]=1; else vis[i][j]=0; } memset(dp,0xf3,sizeof(dp)); dp[sx][sy]=0; for(int i=1;i<=k;i++) { scanf("%d%d%d",&x,&y,&d); if(d==1) for(int j=1;j<=m;j++) work(n,j,y-x+1); if(d==2) for(int j=1;j<=m;j++) work(1,j,y-x+1); if(d==3) for(int j=1;j<=n;j++) work(j,m,y-x+1); if(d==4) for(int j=1;j<=n;j++) work(j,1,y-x+1); } printf("%d",ans); return 0; }

 

posted @ 2018-08-27 09:10  logeadd  阅读(110)  评论(0编辑  收藏  举报