汉诺塔问题

题目: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);
        }		
	}
}


 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值