FOJ 1036 四塔问题

来源:互联网 发布:根据wsdl生成java代码 编辑:程序博客网 时间:2024/06/04 20:02

       来源: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;}


0 0