题目:http://acm.nefu.edu.cn/JudgeOnline/problemshow.php?problem_id=200
题意:改变汉诺塔移动的规则,不允许直接从最左边移到最右边(每次移动一定是移到中间杆或从中间移出),也不
允许大盘放到小盘的上面。现在有个圆盘,至少多少次移动才能把这些圆盘从最左边移到最右边?
分析:移动的大致步骤如下
第1步:把上面的个盘移到第3号杆上
第3步:把第个盘从1移到2
第4步:把前个从3移到1,给第个盘让路
第5步:把第个盘从2移到3
第6步:把前个盘从1移到3,完成移动
设为把个盘从左边第1号杆移到第3号杆所需要的步数,当然也等于从第3号杆移到第1号杆的步数。
则递推关系式为,最终得到。
题目:http://acm.nefu.edu.cn/JudgeOnline/problemshow.php?problem_id=201
题意:仅把上题的题目描述改一下,允许大盘放到小盘的上面。
分析:相比上题,本题放宽了限制,那么移动的步数必定会减少,设为目标答案,可以采取如下方案
第1步:通过上题的方法将前个盘子移动到3号杆
第2步:把剩下的第个盘子和第个盘子移动到2号杆
第3步:将3号杆上的个盘子通过上题方法移动到1号杆
第4步:将2号杆上的第个盘子和第个盘子移动到2号杆
第5步:将1号杆上的个盘子通过上题方法移动到3号杆
得到表达式为,最终得到
题目:http://acm.nefu.edu.cn/JudgeOnline/problemshow.php?problem_id=202
题意:把一个层的汉诺塔涂色,只允许使用红黄蓝三种颜料,三种颜料不一定都用上,禁止出现蓝色相邻的情况,
问一共有多少种涂色方案?
分析:从下往上涂,那么涂到第层就有两种情况,蓝色和非蓝色。把以结束涂蓝色的方案数保存在
里,而不是蓝色结束的方案数保存在里。
如果第层涂蓝色,那么第层一定是以非蓝色结束的,即
如果第层涂非蓝色,那么对第层的颜色没有要求,而且只有红黄两种情况,所以
初始化
上面是经典的汉诺塔问题,当然实际上我们可以对这个问题推广,推广问题表述如下
问题:现在有个杆,在1号杆上有个圆盘,仍然从下到上依次减小,现在要将这个圆盘全部移动到第号杆上
去,问至少需要多少步?
分析:可以这样考虑,令为将个圆盘从1号杆移动到号杆最少需要的步数。
那么可以先将个圆盘从1号杆移动到中间任意一个杆上去,即这步需要最少的步数为,然后再把剩下
的个圆盘通过剩下的个杆移动到第号杆,这步最少需要步数为,最后再把中
间的个圆盘移动到第号杆上去,这步最少步数同样为,枚举所有的,其中,最
终取最小值得到答案。即
当然这是一个递归式,递归出口为,在递归的过程中可以采取记忆化来提升效率。
问题:考虑,那么递归式变为,输出最少需要的步数,
并且需要打印步骤哦。
分析:因为对于,就是经典的汉诺塔问题,其答案为。所以上面的式子其实就是
当然,这个答案是很好求的,直接一个递归加记忆化就行了,关键是打印移动步骤。
代码:
import java.io.*; import java.util.*; public class Hanoi{ public final static int MAX_NUM = 105; public final static long MAX_VAL = 1L << 60; public static int p[] = new int[MAX_NUM]; public static long f[] = new long[MAX_NUM]; public static long step; public static long dfs(int n){ if(f[n] != -1L) return f[n]; if(n == 1){ f[n] = 1; p[n] = 1; return 1; } long minval = MAX_VAL; int curval = 0; for(int i = 1; i < n; i++){ long tmp = 2 * dfs(i) + (1L << (n - i)) - 1; if(tmp < minval){ curval = n - i; minval = tmp; } } p[n] = curval; return f[n] = minval; } public static void Move3(int n, char a, char b, char c){ step++; if(n == 1){ System.out.println(a+"--->"+c); return; } n--; Move3(n, a, c, b); System.out.println(a+"--->"+c); Move3(n, b, a, c); } public static void Move4(int n, char a, char b, char c, char d){ if(n == 1){ System.out.println(a+"--->"+d); step++; return; } Move4(n - p[n], a, c, d, b); Move3(p[n], a, c, d); Move4(n - p[n], b, a, c, d); } public static void main(String[] args){ Scanner cin = new Scanner(System.in); while(cin.hasNextInt()){ int n = cin.nextInt(); Arrays.fill(p, -1); Arrays.fill(f, -1); step = 0; dfs(n); Move4(n, 'A', 'B', 'C', 'D'); System.out.println(step); } } }