6797. 【2014广州市选day2】hanoi

63 篇文章 0 订阅
12 篇文章 0 订阅

Description

你对经典的hanoi塔问题一定已经很熟悉了。有三根柱子,n个大小不一的圆盘,要求大盘不能压在小盘上,初始时n个圆盘都在第一根柱子上,最少要多少步才能挪到最后一根柱子上?
现在我们来将hanoi塔扩展一下,由三根柱子扩展到四根柱子,其余规则不变。例如,3个圆盘,四根柱子A到D,初始时圆盘都A柱上,我们用五步就可以将圆盘都挪到D柱上:
第一步:将圆盘1从A挪到B;
第二步:将圆盘2从A挪到C;
第三步:将圆盘3从A挪到D;
第四步:将圆盘2从C挪到D;
第五步:将圆盘1从B挪到D。
你的任务是写一个程序求解四柱子hanoi塔问题最少要多少步可以解决。

Input

输入只有一行,为一个正整数n。(1<=n<=1000)

Output

输出为一个正整数,代表n盘四柱子hanoi塔问题最少要多少步可以解决。

Sample Input

3

Sample Output

5

Solution

设f[ i ]表示在四柱汉诺塔中,把i个盘子全部移到另一个盘子上的最小步数。

g[ i ]表示三柱汉诺塔中,把i个盘子全部移到另一个盘子上的最小步数。

则有g[ i ]=g[ i-1 ]*2+1,

f[i]=min(f[j]+g[i-j-1])*2+1~~~(j<i)

意思是先把j个盘子在四柱的情况下移到一个柱上,这个柱就不能动了,剩下的i-j-1的盘子在三柱的情况下移到另一个盘子上。

然后将最后一个盘子用一步移到剩下那一个没有盘的柱上,再把其他两个柱上的所有盘子按照刚才移动的方法移到最后一个盘子所在的柱上即可。

答案只有15位,本来是不用高精度的,g[]可以只用存50几个即可。

时间复杂度O(n)。

Code

#include<cstring>
#include<cstdio>
#include<algorithm>
#include<cstdlib>
#define I int
#define ll long long
#define F(i,a,b) for(I i=a;i<=b;i++)
#define Fd(i,a,b) for(I i=a;i>=b;i--)
#define mem(a,b) memset(a,b,sizeof a)
#define N 1005
#define M 100000
using namespace std;
I n,f[N][N],g[N][N],t[N],mn[N];
void R(I &x){
	x=0;I w=1;char c=getchar();
	while(c<'0'||c>'9'){if(c=='-') w=-1;c=getchar();}
	while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
	x*=w;
}
I cmp(){
	if(mn[0]>t[0]) return 1;
	if(mn[0]<t[0]) return 0;
	Fd(l,t[0],1){
		if(t[l]<mn[l]) return 1;
		if(t[l]>mn[l]) return 0;
	}
	return 0;
}
I main(){
	freopen("hanoi.in","r",stdin);
	freopen("hanoi.out","w",stdout);
	R(n);
	F(i,1,n){
		g[i][0]=g[i-1][0];
		F(j,1,g[i][0]) g[i][j]=g[i-1][j]*2;
		g[i][1]++;
		F(j,1,g[i][0]){
			g[i][j+1]+=g[i][j]/M;
			g[i][j]%=M;
		}
		while(g[i][g[i][0]+1]){
			g[i][0]++;
			g[i][g[i][0]+1]+=g[i][g[i][0]]/M;
			g[i][g[i][0]]%=M;
		}
//		g[i]=g[i-1]*2+1;
		mn[0]=1000;
//		mn=1ll<<60;
		F(j,0,i-1){
			F(k,1,t[0]) t[k]=0;
			t[0]=max(f[j][0],g[i-1-j][0]);
			F(k,1,t[0]){
				t[k]+=f[j][k]+g[i-1-j][k];
				t[k+1]+=t[k]/M;
				t[k]%=M;
			}
			while(t[t[0]+1]){
				t[0]++;
				t[t[0]+1]+=t[t[0]]/M;
				t[t[0]]%=M;
			}
			//t[i]=f[j]+g[i-1-j];
			//cmp
			if(cmp()){
				F(k,t[0]+1,mn[0]) mn[k]=0;
				F(k,0,t[0]) mn[k]=t[k];
			}
//			mn=min(mn,f[j]+g[i-1-j]);
		}
		F(j,1,mn[0]) mn[j]*=2;
		mn[1]++;
		F(j,1,mn[0]){
			mn[j+1]+=mn[j]/M;
			mn[j]%=M;
		}
		while(mn[mn[0]+1]){
			mn[0]++;
			mn[mn[0]+1]+=mn[mn[0]]/M;
			mn[mn[0]]%=M;
		}
		F(j,0,mn[0]) f[i][j]=mn[j];
		F(j,0,mn[0]) mn[j]=0;
//		f[i]=mn*2+1;
	}
	printf("%d",f[n][f[n][0]]);
	Fd(i,f[n][0]-1,1) printf("%05d",f[n][i]);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值