首页 > 其他分享 >P2254 [NOI2005] 瑰丽华尔兹 题解

P2254 [NOI2005] 瑰丽华尔兹 题解

时间:2024-01-23 15:23:56浏览次数:18  
标签:head ++ 题解 P2254 NOI2005 int tail abs now

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

标签:head,++,题解,P2254,NOI2005,int,tail,abs,now
From: https://www.cnblogs.com/Ishar-zdl/p/17982564

相关文章

  • P2569 [SCOI2010] 股票交易 题解
    P2569[SCOI2010]股票交易搬运工稍微复杂一点的单调队列优化DP直接设\(f_{i\j}\)表示在第\(i\)天,手上还剩\(j\)个股票时的最大收入。容易写出状态转移方程:\(f_{i\j}=max\{f_{k\t}+(t-j)\cdotw\}\),这样不好看,我们可以拆成这样的形式:\[f_{i\j}=max\{f_{k\t}+t\cdo......
  • P4899 [IOI2018] werewolf 狼人 题解
    因为我记忆力不好,经常遇到之前做过的题一下子想不起来,所以打算基本上给每个比较有意思的题写题解,同时造福后代。这是werewolf,它是furry,很可爱题意:一张无向图,点有点权,每次询问从\(u\)到\(v\)的路径中,是否存在一条先走点权大于等于\(L\),再走点权小于等于\(R\)的路径。思路......
  • hugeの张江蔡唐氏模拟赛题解
    晚三huge不让去一机房,说便于管理,我的评价是:唐氏况且二机房没有luogu,改完题后没事干(指写不了狼人),遂写个没人看的题解。T1纯哈希,不写。T2纯tarjan,一直没学,不写。T3比较套路的双指针,赛时降智题意:给定由\(B\)和\(R\)组成的字符串,环形结构,每次可以交换相邻,问最少多少次可以......
  • 01.22 ARC170 题解慢报
    补完了,来发题解慢报。AB就不写了。CPrefixMexSequence考虑DP,\(f(i,j)\)表示前\(i\)个数,填了\(j\)个不同的数。如果\(s_{i+1}=1\)那么这位唯一确定,只需要保证\(j<m\)即可转移到\(f(i+1,j+1)\);如果\(s_{i+1}=0\)那么可以选旧的数也可以选新的数,分别转移即可。D......
  • 「Daily OI Round 1」block 题解
    设\(c_u\)表示节点\(u\)的颜色,\(f_u\)表示只考虑原树中\(u\)的子树中的点、选择点\(u\)的方案数。对于儿子\(v\),可以选择同色儿子节点,也可以跳过这个儿子节点,选择\(v\)的与\(u\)同色的儿子节点\(w\),故状态转移方程为:\[f_u=\prod[c_u=c_v]f_v+\left(\prod[c_u=c_w]......
  • CF1424M Ancient Language 题解
    1.题意描述一本字典中有\(r\)\((1\leqr\leq10^6)\)个单词,单词长度不超过$10^3$,所有字母都是小写英文字母,但字母排序不是按英文字母排序,求所有出现字母的一种排序,如果不存在,输出"IMPOSSIBLE"。2.题目分析由排序规则可知,对于字符串\(s\)和\(t\),\(s\)排在\(t\)......
  • CF618C Constellation 题解
    题意描述给定\(n\)个点(\(n\leq10^5\)),找到三个点满足这三个点不在同一直线上且三个点构成的三角形中不包含其他点,保证所有点不会在同一直线上。题目分析首先必然每一个点都可以作为一组解中的点。不妨其中一个点编号为\(1\)。找一个点作为第二个点,为了使没有点在这条边上,这......
  • CF1036F Relatively Prime Powers 题解
    题目分析对于一个不合法的数\(x(x\ge2)\),设\(x=\prodp_i^{r_i}\),令\(g=\gcd(r_1,r_2,\ldots,r_k)\),则\(x=\left(\prodp_i^{r_i/g}\right)^g\),所以\(x\)是一个正整数的\(g\)次方。所以可以枚举上文的\(g\),把每一类不合法方案排除掉,就是答案。设\(f(i)\)表示\(2\)......
  • AT_arc098_d Donation 题解
    一道在kruskal重构树上DP的题。首先,捐钱比较难想,可以反过来思考倒着走领钱的思路。显然,在第一次经过一个节点的时候领钱是最优的,对于节点\(i\),令\(c_i=\max\{a_i-b_i,0\}\),若当前的钱数是\(v\),到节点\(i\)的条件是\(v\gec_i\),如果不满足则把\(v\)补充到\(c_i\),同......
  • P5618 SDOI2015 道路修建题解
    题目分析虽然数据范围只有\(n\le60000\),但是完全可以直接用线段树做。首先考虑那种状态的图在左边和右边加入节点和边之后可以连通。容易发现,这种图有这两个性质:至少有一条路径,经过最左端和最右端中的点。所有点至少和最左端和最右端的点连通。于是可以划分成以下几种状态......