51nod 1800 汉诺塔

160 篇文章 0 订阅
61 篇文章 0 订阅

LYK最近在研究汉诺塔问题。
汉诺塔(又称河内塔)问题是源于印度一个古老传说的益智玩具。大梵天创造世界的时候做了三根金刚石柱子,在一根柱子上从下往上按照大小顺序摞着64片黄金圆盘。大梵天命令婆罗门把圆盘从下面开始按大小顺序重新摆放在另一根柱子上。并且规定,在小圆盘上不能放大圆盘,在三根柱子之间一次只能移动一个圆盘。
在这个问题中,黄金圆盘的数量从原来的64片变成了n片。
大家都知道对于n片圆盘的汉诺塔问题的最少操作次数是2^n-1次。
现在LYK将初始时的n片圆盘按照规则随机摆放(也就是说每片圆盘一开始可能在3根柱子上的任意一根,且小圆盘上没有大圆盘),共有3^n摆法。
LYK想将所有圆盘都重新摆放进第三根柱子。
当然最优方案显然是唯一的。
请你求出这3^n种不同的初始状态中,存在几种使得最少操作次数恰好等于m。

在样例中,共有2片圆盘,摆放状态共有9种,只有当大圆盘在第3根柱子,小圆盘在第1或者第2根柱子时,最少操作次数恰好等于1。因此答案为2。
Input
一行两个整数n,m(1<=n,m<10^100)。
Output
一行表示答案
Input示例
2 1
Output示例
2
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

高精度+DP+神奇的思路~

花了我六十……好心疼啊QAQ

我们用f[i][j]表示j个圆盘,用i步的方案数,那么

当i在第三根柱子上时,=f[i-1][j];

其余情况,我们把所有小于它的圆盘移到1或2柱子上,移动i后,再用(2^(i-1)-1)步移回来,=f[i-1][j-2^(i-1)]。

所以f[i][j]=f[i-1][j]+f[i-1][j-2^(i-1)]*2。

观察到n<=10^100,n趋于无穷大,这时i和i-1近似相等,我们发现第一维可以忽略,f[j]=2^(n中1的个数)。

所以我们用高精度求一下这个值就好了……

要注意特判0的情况。


#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;

int n,m,a[1001],f[1001],tot,cnt;
char s[1001],s1[1001];

int cal()
{
	int tot=0;
	for(int i=0;i<n;i++) tot=tot*10+s1[i]-'0';
	return tot;
}

int main()
{
	scanf("%s%s",s1,s);
	n=strlen(s);
	for(int i=0;i<n;i++) a[n-i]=s[i]-'0';
	f[1]=tot=1;
	for(;n>1 || a[1];cnt++)
	{
		if(a[1]&1)
		{
			for(int i=1;i<=tot;i++) f[i]*=2;
			for(int i=1;i<=tot;i++) if(f[i]>9) f[i+1]++,f[i]-=10;
			if(f[tot+1]) tot++;
		}
		for(int i=n,z=0;i;i--) z=z*10+a[i],a[i]=z>>1,z&=1;
		if(!a[n]) n--;
	}
	n=strlen(s1);
	if(n<=3 && cnt>cal()) puts("0");
	else for(int i=tot;i;i--) printf("%c",f[i]+'0');
	return 0;
}


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值