回溯策略的汉诺塔问题

问题描述#

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

问题分析#

第一步构造状态空间#

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

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

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

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

Copy
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 @   小帆敲代码  阅读(277)  评论(0编辑  收藏  举报
编辑推荐:
· .NET 原生驾驭 AI 新基建实战系列:向量数据库的应用与畅想
· 从问题排查到源码分析:ActiveMQ消费端频繁日志刷屏的秘密
· 一次Java后端服务间歇性响应慢的问题排查记录
· dotnet 源代码生成器分析器入门
· ASP.NET Core 模型验证消息的本地化新姿势
阅读排行:
· 开发的设计和重构,为开发效率服务
· ThreeJs-16智慧城市项目(重磅以及未来发展ai)
· 从零开始开发一个 MCP Server!
· .NET 原生驾驭 AI 新基建实战系列(一):向量数据库的应用与畅想
· Ai满嘴顺口溜,想考研?浪费我几个小时
点击右上角即可分享
微信分享提示
CONTENTS