单调队列优化DP || [NOI2005]瑰丽华尔兹 || BZOJ 1499 || Luogu P2254

时间:2021-03-04 08:42:27

题外话:题目极好,做题体验极差

题面:[NOI2005]瑰丽华尔兹

题解:

F[t][i][j]表示第t时刻钢琴位于(i,j)时的最大路程
F[t][i][j]=max(F[t-1][i][j],F[t-1][a][b]+1) (mp[i][j]可以到达,(a,b)直接到(i,j)之间没有家具,即路径合法)
因为船的倾斜是连续的,所以可以考虑按时间段来进行dp
F[t][i][j]表示前t个时间段结束后,钢琴位于(i,j)的最大路程
F[t][i][j]=max(F[t][i][j],F[t-1][a][b]+Dis(a,b,i,j)) (mp[i][j]可达,Dis(a,b,i,j)<=T[t]-S[t]+1,(a,b)直接到(i,j)之间没有家具,即路径合法)
考虑使用单调队列优化dp
以下“OK”意味着mp[i][j]不出地图,且(i,j)上无家具,是可以到达的合法位置,且路径合法
路径合法可以通过在单调队列时遇到mp[i][j]=='x'的情况直接清空队列来快速实现,当然也可以通过写前缀和来判断实现
注意在写单调队列时入队应该放在维护F[t][i][j]前,因为可以停留在(i,j)
Case 1:D[t]==1
此时船向北倾斜,则b=j(i大到i小)
F[t][i][j]=max(F[t][i][j],F[t-1][a][j]+a-i) (OK,a-i<=T[t]-S[t]+1)
即维护:max(F[t-1][a][j]+a)-i (a<=T[t]-S[t]+1+i)
Case 2:D[t]==2
此时船向南倾斜,则b=j(i小到i大)
F[t][i][j]=max(F[t][i][j],F[t-1][a][j]+i-a) (OK,i-a<=T[t]-S[t]+1)
即维护:max(F[t-1][a][j]-a)+i (a>=i-T[t]+S[t]-1)
Case 3:D[t]==3
此时船向西倾斜,则a=i(j从大到小)
F[t][i][j]=max(F[t][i][j],F[t-1][i][b]+b-j) (OK,b-j<=T[t]-S[t]+1)
即维护:max(F[t-1][i][b]+b)-j(b<=T[t]-S[t]+1+j)
Case 4:D[t]==4
此时船向东倾斜,则a=i(j从小到大)
F[t][i][j]=max(F[t][i][j],F[t-1][i][b]+j-b) (OK,j-b<=T[t]-S[t]+1)
即维护:max(F[t-1][i][b]-b)+j (b>=j-T[t]+S[t]-1)
对以上使用单调队列进行优化

代码:

 #include<cstdio>
#include<iostream>
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
using namespace std;
const int maxn=,maxm=,maxk=,maxt=4e4+;
const int inf=<<;
int N,M,K,X0,Y0,S[maxk],T[maxk],D[maxk];
int F[maxk][maxn][maxm],f1,f2,ans=;
char mp[maxn][maxm];
struct Node{ int data,x; }que[maxn];
int main(){
scanf("%d%d%d%d%d",&N,&M,&X0,&Y0,&K);
for(int i=;i<=N;i++)
scanf("%s",mp[i]+);
for(int i=;i<=K;i++)
scanf("%d%d%d",&S[i],&T[i],&D[i]);
for(int t=;t<=K;t++)
for(int i=;i<=N;i++)
for(int j=;j<=M;j++)
F[t][i][j]=-inf;
for(int t=;t<=K;t++)
F[t][X0][Y0]=;
for(int t=;t<=K;t++){
if(D[t]==){
for(int j=;j<=M;j++){
f1=,f2=;
for(int i=N;i>=;i--){
if(mp[i][j]=='x') {
f1=,f2=;
continue;
}
while(f1<=f2 && que[f1].x>T[t]-S[t]++i) f1++;
while(f1<=f2 && F[t-][i][j]+i>=que[f2].data) f2--;
que[++f2].data=F[t-][i][j]+i; que[f2].x=i;
if(f1<=f2) F[t][i][j]=max(F[t][i][j],que[f1].data-i);
}
}
}
else if(D[t]==){
for(int j=;j<=M;j++){
f1=,f2=;
for(int i=;i<=N;i++){
if(mp[i][j]=='x') {
f1=,f2=;
continue;
}
while(f1<=f2 && que[f1].x<i-T[t]+S[t]-) f1++;
while(f1<=f2 && F[t-][i][j]-i>=que[f2].data) f2--;
que[++f2].data=F[t-][i][j]-i; que[f2].x=i;
if(f1<=f2) F[t][i][j]=max(F[t][i][j],que[f1].data+i);
}
}
}
else if(D[t]==){
for(int i=;i<=N;i++){
f1=,f2=;
for(int j=M;j>=;j--){
if(mp[i][j]=='x') {
f1=,f2=;
continue;
}
while(f1<=f2 && que[f1].x>T[t]-S[t]++j) f1++;
while(f1<=f2 && F[t-][i][j]+j>=que[f2].data) f2--;
que[++f2].data=F[t-][i][j]+j; que[f2].x=j;
if(f1<=f2) F[t][i][j]=max(F[t][i][j],que[f1].data-j);
}
}
}
else{// D[t]==4
for(int i=;i<=N;i++){
f1=,f2=;
for(int j=;j<=M;j++){
if(mp[i][j]=='x') {
f1=,f2=;
continue;
}
while(f1<=f2 && que[f1].x<j-T[t]+S[t]-) f1++;
while(f1<=f2 && F[t-][i][j]-j>=que[f2].data) f2--;
que[++f2].data=F[t-][i][j]-j; que[f2].x=j;
if(f1<=f2) F[t][i][j]=max(F[t][i][j],que[f1].data+j);
}
}
}
}
for(int i=;i<=N;i++)
for(int j=;j<=M;j++)
ans=max(ans,F[K][i][j]);
printf("%d\n",ans);
return ;
}

By:AlenaNuna