[BZOJ 1499] 瑰丽华尔兹
Link:https://www.lydsy.com/JudgeOnline/problem.php?id=1499
Solution :
能立即发现这是和动态规划相关的题目
令f[t][i][j]表示第t段时间时,钢琴位于(i,j)处时,从第1段时间到第t段时间的最长滑行路程。
f[t][i][j]=max{ f[t−1][i last][j last] + dist{ (i last,j last) , (i,j) } }
由于四个方向的处理类似,这里以向右举例:
f[t][i][j]=max{ f[t−1][i][j′] + (j−j′) }=j+max{f[t−1][i][j′]−j′} , j−j′≤end-start+1
对于每个f[t][i][j],我们就是找到最大的max{f[t−1][i][j′]−j′},同时使得j−j′≤end-start+1
由于j-j'单调递减,每次需要弹出队首元素,我们使用单调队列而非单调栈来维护这个序列
Code:
#include <bits/stdc++.h> using namespace std; const int MAXN=300; const int INF=1<<27; inline int read() { char ch;int num,f=0; while(!isdigit(ch=getchar())) f|=(ch=='-'); num=ch-'0'; while(isdigit(ch=getchar())) num=num*10+ch-'0'; return f?-num:num; } int n,m,X,Y,k,dp[2][MAXN][MAXN],d=0; //滚动数组优化 char dat[MAXN][MAXN]; int st,end,dir,res=0,que[MAXN],t[MAXN]; int dx[]={0,-1,1,0,0},dy[]={0,0,0,-1,1}; void solve(int x,int y) { int l=1,r=0,now=1; while(x>=1 && x<=n && y>=1 && y<=m) { if(dat[x][y]=='x') l=1,r=0; while(l<=r && now-t[l]>end-st+1) l++; //弹出队首 while(l<=r && que[r]<dp[d^1][x][y]-now) r--; //保证递减的单调性 que[++r]=dp[d^1][x][y]-now;t[r]=now; dp[d][x][y]=que[l]+now; res=max(res,dp[d][x][y]);now++; x+=dx[dir];y+=dy[dir]; } } int main() { cin >> n >> m >> X >> Y >> k; for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) cin >> dat[i][j]; memset(dp,0x80,sizeof(dp)); dp[0][X][Y]=0; for(int i=1;i<=k;i++) { st=read(),end=read(),dir=read(); d^=1; if(dir==1) for(int i=1;i<=m;i++) solve(n,i); else if(dir==2) for(int i=1;i<=m;i++) solve(1,i); else if(dir==3) for(int i=1;i<=n;i++) solve(i,m); else for(int i=1;i<=n;i++) solve(i,1); } cout << res; return 0; }
Review:
1、对于有决策单调性的DP,
考虑利用其单调性优化(斜率优化,单调栈,单调队列)
同时注意考虑限制条件,判断何时弹出队首
2、如果DP的数组每一层只用一次,使用滚动数组优化空间