博客园 首页 私信博主 显示目录 隐藏目录 管理 动画

BZOJ.1019.[SHOI2008]汉诺塔(递推)

题目链接

按照优先级移动,操作序列显然是唯一的。
普通的汉诺塔是将n-1个盘子移到中间柱,再将第n个盘子移到目标柱,再将n-1个移到目标柱上的递归过程。
对于本题移动方法还是基本一样的。考虑递推,从i-1个盘子推i个盘子。
p[i][x]表示将x柱上的前i个盘子移走,按顺序操作最终会到哪个柱子上,f[i][x]则表示这i个盘子到p[i][x]需要移动的次数。答案即 f[n][0]。
对于 f[i][x],先要将i-1个移到y去(设 y=p[i-1][x]),那么第i个就要移到第三个柱子z上(柱子用0,1,2标号,那么 z=3-x-y)。
这时那i-1个盘子的状态是[i-1][y]。这也要讨论。
若 p[i-1][y]=z,即i-1个直接放到i上,那么 f[i][x]=f[i-1][x]+1+f[i-1][y], p[i][x]=z。
若 p[i-1][y]=x(又回到x),因为 p[i-1][x]=y,所以将i移到y上,再将i-1移到y,f[i][x]=2*f[i-1][x]+2+f[i-1][y], p[i][x]=y。
这样转移方程就有了。
初始化 f[1][0/1/2]=1,g[1][0/1/2]就是给定的优先级。
因为 g[i][]会被 g[i-1][]确定,即会被优先级确定。(真的挺妙。。)

//824kb	40ms
#include <cstdio>
const int N=33;

int n,p[N][3];
bool vis[5];
long long f[N][3];

int main()
{
	scanf("%d",&n);
	char s[5];
	for(int i=1; i<=6; ++i){
		scanf("%s",s);
		if(!vis[s[0]-'A']) p[1][s[0]-'A']=s[1]-'A',vis[s[0]-'A']=1;
	}
	f[1][0]=f[1][1]=f[1][2]=1;
	for(int i=1; i<n; ++i)
		for(int y,z,x=0; x<3; ++x)
		{
			y=p[i][x], z=3-x-y;
			if(p[i][y]==z) f[i+1][x]=f[i][x]+1+f[i][y], p[i+1][x]=z;
			else /*if(p[i][y]==x)*/ f[i+1][x]=(f[i][x]<<1)+2+f[i][y], p[i+1][x]=y;
		}
	
	printf("%lld",f[n][0]);

	return 0;
}
posted @ 2018-04-28 21:31  SovietPower  阅读(167)  评论(0编辑  收藏  举报