P2254 [NOI2005] 瑰丽华尔兹 题解

P2254 [NOI2005] 瑰丽华尔兹

搬运工
单调队列优化DP
还是先朴素,设 \(f_{t\ i\ j\ d}\) 表示在第 \(t\) 个时刻,在 \(i,j\) 位置上,上一步是停留还是滑动的最大步数。这个就是四个方向随便转移。
\(T_{max}=4*10^4\) 这么做肯定不行。发现 \(k\) 很小,只有 \(200\) ,所以不妨让 \(k\) 做状态,这样时间空间都可以接受了,这时候的转移就是从 \(len_i\) 个距离以内的方向转移,然后显然这时候的停留和滑动的状态已经没用了,所以最后 \(f_{t\ i\ j}\) 表示在第 \(t\) 个时间段,在第 \(i,j\) 个位置时的最大步数,状态转移方程如下:

\[f_{t\ i\ j}=max(f_{t-1\ k\ j}+abs(i-k)) \]

这里 \(k\) 是有范围的:\(i-len\le k\le i\) ,这个方程和范围只针对方向为上时,其他三个同理,不多赘述。

#include<bits/stdc++.h>
inline int read(){
	char ch=getchar();int x=0,f=1;
	for(;ch<'0'||ch>'9';ch=getchar())if(ch=='-')f=-1;
	for(;ch>='0'&&ch<='9';ch=getchar())x=(x<<3)+(x<<1)+(ch^48);
	return x*f;
}
const int N=205;
bool D[N][N];
int n,m,x,y,k,f[2][N][N],now,l[N],r[N],dir[N],q[N],head,tail;
int main(){
	// freopen("in.in","r",stdin);freopen("out.out","w",stdout);
	std::cout.tie(0);
	n=read(),m=read(),x=read(),y=read(),k=read();
	memset(f,-0x3f,sizeof(f));
	for(int i=1;i<=n;++i)
		for(int j=1;j<=m;++j){
			char ch=getchar();
			while(ch!='.'&&ch!='x')ch=getchar();
			if(ch=='x')D[i][j]=true;
		}
	for(int i=1;i<=k;++i)l[i]=read(),r[i]=read(),dir[i]=read();
	f[now][x][y]=0;
	for(int t=1;t<=k;++t,now^=1){
		int len=r[t]-l[t]+1;
		if(dir[t]==1){
			for(int j=1;j<=m;++j){
				head=1,tail=0;
				for(int i=n;i;--i){
					if(D[i][j]){head=1,tail=0;continue;}
					while(head<=tail&&f[now][i][j]-abs(i-n)>=f[now][q[tail]][j]-abs(q[tail]-n))tail--;
					q[++tail]=i;
					if(abs(q[head]-i)>len)head++;
					f[now^1][i][j]=f[now][q[head]][j]-abs(q[head]-n)+abs(i-n);
				}
			}
		}
		if(dir[t]==2){
			for(int j=1;j<=m;++j){
				head=1,tail=0;
				for(int i=1;i<=n;++i){
					if(D[i][j]){head=1,tail=0;continue;}
					while(head<=tail&&f[now][i][j]-abs(i-1)>=f[now][q[tail]][j]-abs(q[tail]-1))tail--;
					q[++tail]=i;
					if(abs(q[head]-i)>len)head++;
					f[now^1][i][j]=f[now][q[head]][j]-abs(q[head]-1)+abs(i-1);
				}
			}
		}
		if(dir[t]==3){
			for(int i=1;i<=n;++i){
				head=1,tail=0;
				for(int j=m;j;--j){
					if(D[i][j]){head=1,tail=0;continue;}
					while(head<=tail&&f[now][i][j]-abs(j-m)>=f[now][i][q[tail]]-abs(q[tail]-m))tail--;
					q[++tail]=j;
					if(abs(q[head]-j)>len)head++;
					f[now^1][i][j]=f[now][i][q[head]]-abs(q[head]-m)+abs(j-m);
				}
			}
		}
		if(dir[t]==4){
			for(int i=1;i<=n;++i){
				head=1,tail=0;
				for(int j=1;j<=m;++j){
					if(D[i][j]){head=1,tail=0;continue;}
					while(head<=tail&&f[now][i][j]-abs(j-1)>=f[now][i][q[tail]]-abs(q[tail]-1))tail--;
					q[++tail]=j;
					if(abs(q[head]-j)>len)head++;
					f[now^1][i][j]=f[now][i][q[head]]-abs(q[head]-1)+abs(j-1);
				}
			}
		}
	}
	int ans=-1;
	for(int i=1;i<=n;++i)for(int j=1;j<=m;++j)ans=std::max(ans,f[now][i][j]);
		std::cout<<ans<<'\n';
}

是一道挺好的单调队列优化DP

posted @ 2024-01-23 15:18  Ishar-zdl  阅读(58)  评论(0)    收藏  举报