回溯策略的汉诺塔问题

问题描述

把圆盘从下面开始按大小顺序重新摆放在另一根柱子上。并且规定,在小圆盘上不能放大圆盘,在三根柱子之间一次只能移动一个圆盘。

问题分析

第一步构造状态空间

假设第i个圆盘的在第Ai个杆子上。则状态可以表示为(A1,A2,A3,...,An)。
假设此时只有两个盘子。则状态空间表示为下图:

(close表中是已经重复过的记录,避免无限循环)

第二步确定状态转移的规则。

根据题意进行状态转移有一个条件,就是大盘子不能放在小盘子上。除此之外,出现在close表中的记录也不能重复出现。用递归进行深度的搜索。用循环控制回溯。

public class AI {
	private static Set<String> list=new HashSet<String>();//保存已经出现过的状态
	private static ArrayList<String> slist=new ArrayList<String>();//保存解状态
	public static void main(String[] args) {
		//建立初始状态
		Ta[] ts=new Ta[2];
		Gan[] gs=new Gan[3];
		StringBuilder sb=new StringBuilder();
		for(int i=0;i<ts.length;i++) {
			ts[i]=new Ta(i,(char)(i+'A')+"",0);
			sb.append(0);
		}
		list.add(sb.toString());
		for(int i=0;i<3;i++) {
			gs[i]=new Gan();
		}
		
		dfs(ts,gs,0);
		//打印解状态
		System.out.println(slist);
	}
	public static boolean dfs(Ta[] ts,Gan[] gs,int index) {
		for(int j=0;j<3;j++) {
			if(ts[index].index!=j) {
				int now=ts[index].index;
				StringBuilder sb=new StringBuilder();
				for(Ta t:ts) {
					if(t!=ts[index])
						sb.append(t.index);
					else 
						sb.append(j);
				}
				if(ts[index].size<gs[j].getTopSize()) {
					boolean flag=false;
					for(String s:list) {
						if(s.equals(sb.toString())) {
							flag=true;
							break;
						}
					}
					if(!flag) {
						//移动到j
					ts[index].index=j;
					gs[now].list.remove(ts[index]);
					gs[j].list.add(ts[index]);
					list.add(sb.toString());
					slist.add(ts[index].name+"移动到"+(ts[index].index+1));
					boolean f=false;
					for(int i=0;i<ts.length;i++) {//回溯
						if(i!=index)
							{
							f=dfs(ts,gs,i);//递归
								if(f) {
									return true;
								}
							}
					}
					//成功条件
					if(gs[1].list.size()==ts.length&&gs[1].getTop()==ts[0]) {
						return true;
					}
					//放回原位
					gs[j].list.remove(ts[index]);
					ts[index].index=now;
					//slist.add(ts[index].name+"移动到"+now);
				}
				}
			}
		}
		return false;
	}
}
/**
 * 汉诺塔
 * @author zyf
 *
 */
class Ta{
	int size;//尺寸
	String name;
	int index;//在第index个杆子上
	public Ta(int size, String name, int index) {
		this.size = size;
		this.name = name;
		this.index = index;
	}
}
/**
 * 柱子
 * @author zyf
 *
 */
class Gan{
	ArrayList<Ta> list=new ArrayList<Ta>();
	public boolean isEmp() {
		return list.size()==0;
	}
	public int getTopSize() {
		if(!isEmp())
			return list.get(list.size()-1).size;
		return Integer.MAX_VALUE;
	}
	public Ta getTop() {
		if(!isEmp())
			return list.get(list.size()-1);
		return null;
	}
}

测试结果

两个:

三个:
[A移动到2, B移动到3, A移动到1, B移动到2, A移动到2, C移动到3, A移动到1, B移动到3, A移动到2, B移动到1, A移动到1, C移动到2, A移动到2, B移动到3, A移动到1, B移动到2, A移动到2]

总结

其实刚开始的时候出现了很多问题就是递归可能需要一个返回值其次如果不成功就要回溯这一点没有流畅的写出来。

posted @ 2020-10-03 11:39  小帆敲代码  阅读(256)  评论(0编辑  收藏  举报