洛谷2254 BZOJ1499 瑰丽华尔兹题解
一个很容易想到的做法就是用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; }