题目:http://acm.nefu.edu.cn/JudgeOnline/problemshow.php?problem_id=200
题意:改变汉诺塔移动的规则,不允许直接从最左边移到最右边(每次移动一定是移到中间杆或从中间移出),也不
允许大盘放到小盘的上面。现在有个圆盘,至少多少次移动才能把这些圆盘从最左边移到最右边?
分析:移动的大致步骤如下
个盘移到第3号杆上
个盘从1移到2
个从3移到1,给第
个盘让路
个盘从2移到3
个盘从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);
}
}
}