FOJ 1036 四塔问题

       来源:http://acm.fzu.edu.cn/problem.php?pid=1036

       概述:经典的汉诺塔使用到3个柱子ABC,不妨称为“三塔问题”,记把n个盘子从A移到C需要H(n)次操作。那么,四塔问题中4个柱子ABCD,记把n个盘子从A移到D需要F(n)次操作。请求解F(n),答案对1e4取余。

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

       求解时,“『落』常笑鹰”和“原来我们都只是菜鸟”给予了很大的帮助,同时参阅了该篇文章:http://blog.csdn.net/wsqgwp/article/details/9164399

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

理论分析

       先理清三塔问题的思路。要完成H(n),就要先把n-1个盘子从A移到B(操作次数相当于把n-1个盘子从A移到C,即H(n-1)),接着把A中剩下那个,也是最大的盘子移到C,再把B中n-1个盘子移到C。从移动方法,可以看出递推公式H(n)=2H(n-1)+1。再根据H(1)=1,不难得出通项式,下面为推导过程:


       再来看四塔问题。对于F(n),我们可以先把A中的j(0 ≤ j < n)个盘子移到B,需要F(j)次操作,然后在柱子ACD中,完成剩下的n-j个盘子操作——把A中n-j个盘子,利用C,移动到D,这等价于三塔问题,所以操作次数为H(n-j),再把B中的j个盘子移到D,操作F(j)次。所以有n种递推公式

在这n种递推方案中,我们要找出操作次数最小的最优解,故

       从四塔问题,我们可以进一步得到n个盘子,m(m>3)个柱子的一般递推式(双递归)


       此式的求解,用到两个性质


       我的能力,目前也只能推导到这里了。但这些理论对于本题的求解并没有多大的帮助,一方面用这些递推式来求n最大为5万的4塔问题,显然整型数据会溢出;另一方面,对中间值取余,也会造成无法判断哪种组合才是真正的最小值。故下面也只能用“投机取巧”的方法,做出答案,并背住结论了。

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

实际求解

       我们用理论分析出的F(n)公式,求出前几项值,来发现是否有数值递增的规律。如果有,那么通过每次递增值的方法做取余运算比用通项式还方便(因为通项式求解很可能还要写快速幂模板)。F(n)的求解可以列表手算,更方便的,也可以使用Excel。算出后,再算F(n)与F(n+1)的差,发现确实有规律性:


       发现F(n)的递增规律为:一个2^0,两个2^1,三个2^2,四个2^3,接下来又是2^4,故猜想后续的规律——5个2^4,...,k+1个2^k,直到加到第n项。这个猜想从实践结果来看是正确的,但不会证明。

       利用这规律,很容易实现编程求解。我的代码中用K代表当前加到第2^(K-1)部分,k代表加到2^(K-1)中第k项。把2^(K-1)值记为t,只要每次对t自乘2,并对1万取余即可。

#include <iostream>
using namespace std;

const int MAXN = 50001;

void init(int F[])
{
	int k = 0, K = 1, t = 1;
	F[0] = 0;
	for (int i = 1; i < MAXN; i++)
	{
		F[i] = (F[i-1] + t) % 10000;
		k++;
		if (k == K)
		{
			K++;
			k = 0;
			t = t*2%10000;
		}
	}
}

int main()
{
	int n, F[MAXN];
	init(F);
	while (cin >> n) cout << F[n] << endl;
	return 0;
}


  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值