洛谷 P1242 新汉诺塔
原题链接
题目描述
设有n个大小不等的中空圆盘,按从小到大的顺序从1到n编号。将这n个圆盘任意的迭套在三根立柱上,立柱的编号分别为A、B、C,这个状态称为初始状态。
现在要求找到一种步数最少的移动方案,使得从初始状态转变为目标状态。
移动时有如下要求:
·一次只能移一个盘;
·不允许把大盘移到小盘上面。
输入输出格式
输入格式:
文件第一行是状态中圆盘总数;
第二到第四行分别是初始状态中A、B、C柱上圆盘的个数和从上到下每个圆盘的编号;
第五到第七行分别是目标状态中A、B、C柱上圆盘的个数和从上到下每个圆盘的编号。
输出格式:
每行一步移动方案,格式为:move I from P to Q
最后一行输出最少的步数。
输入输出样例
5 3 3 2 1 2 5 4 0 1 2 3 5 4 3 1 1
move 1 from A to B move 2 from A to C move 1 from B to C move 3 from A to B move 1 from C to B move 2 from C to A move 1 from B to C 7
说明
圆盘总数≤45
每行的圆盘描述是从下到上的圆盘编号
题解
这道题目是经典的汉诺塔问题,没什么技术,但思维难度较高,如果条件判断太多则编码难度也会较高
首先,我们很容易想到一种假算法:(一定要注意它是错的,但对真算法有启发意义)
因为大盘子无法在小盘子上移动,而大盘子移动好之后又不会影响小盘子(这是本题所有操作的前提),故可以从大盘子开始移动。
我们从第N号盘子开始操作,计当前盘子为i号,如果它在原位置,那么就跳过,否则就将1~i-1号盘子都移动到不会动用的盘子,将目标盘子空出,然后将i号盘子放进去。经过N次操作,一定可以完成,但不能保证最优。
如图所示,如果我们要将第1号桩上的1个大盘子移到2号桩,那么我们就先将1、2号桩上所有比i号盘子小的盘子都移到第3号桩
实现这一过程很简单,只要每次将1~i-1号盘子移动到闲置的位置,然后移动即可,代码也十分简单
1 #include<bits/stdc++.h> 2 using namespace std; 3 typedef long long LL; 4 const int INF=1e9+7,MAXN=50; 5 int N,cur[MAXN],goal[MAXN],ans; 6 char tran[]={' ','A','B','C'};/*translate*/ 7 inline void input(int *array,int opt){ 8 int ii,jj; 9 scanf("%d",&ii); 10 while(ii--){ 11 scanf("%d",&jj); 12 array[jj]=opt; 13 } 14 } 15 void dfs(int from/*from idx*/,int to/*to pos*/){/*move the "from"-th plate to position "to"*/ 16 if(cur[from]==to) 17 return; 18 int other=6-cur[from]-to; 19 for(int i=from-1;i>=1;i--) 20 dfs(i,other); 21 printf("move %d from %c to %c\n",from,tran[cur[from]],tran[to]); 22 cur[from]=to; 23 ans++; 24 } 25 int main(){ 26 scanf("%d",&N); 27 input(cur,1); 28 input(cur,2); 29 input(cur,3); 30 input(goal,1); 31 input(goal,2); 32 input(goal,3); 33 34 for(int i=N;i>=1;i--) 35 dfs(i,goal[i]); 36 printf("%d",ans); 37 return 0; 38 }
View Code
但正如上面所说的,这是一个假算法。我们认定如图的变换方法是正确的,是建立在汉诺塔的性质的基础上的。在汉诺塔中,未归位的盘子都是连续叠加的(如下图1),不可能出现如图2的情况
这样的话,无论将这个连续的塔从哪里移动到哪里都是等价的,故开始给出的假算法是成立的。
然而可惜的是,我们的塔最初是随机摆放的,所以会有另一种方法
可以证明,没有其他移动方法了。但是否每次都要用两种方法呢?显然不是。观察两个图组可以发现,在进行了一次操作后,就还原到了汉诺塔的基本结构:一串连续的盘子,就可以用常规的方法操作了。
那图组2和图组1的过程又怎么实现呢?我们需要三种操作
- move(idx,from,to):当1~idx在同一个桩子上时,将它们移到to号桩子
- merge(idx,to):当1~idx不在同一个桩子上时,将它们移到to号桩子
- solve(idx,to):当1~idx归位
move函数的实现很简单,递归将1~idx-1号盘子移到闲置的位置,将idx号盘子移到to,再将1~idx-1号盘子移到to即可
void mov(int idx,int from,int to,int other){ /*move 1~"idx"-th to "to" in a heap*/if(!idx)return;mov(idx-1,from,other,to);ans[st][++sz[st]]=(state){idx,from,to};mov(idx-1,other,to,from); }
merge函数也不难,将递归将1~idx-1号盘子移到闲置的位置(用merge),将idx号盘子移到to,再将1~idx-1号盘子移到to即可(用move)
void merg(int idx,int to){ /*move 1~"idx"-th to "to" not in a heap*/if(!idx)return;if(cur[idx]==to)return merg(idx-1,to);int other=6-cur[idx]-to;merg(idx-1,other);ans[st][++sz[st]]=(state){idx,cur[idx],to};mov(idx-1,other,to,cur[idx]); }
solve函数的思想同上
void solve(int idx,int to){ /*put 1-"idx"-th into its place*/if(!idx)return;if(goal[idx]==to)return solve(idx-1,to);int other=6-goal[idx]-to;mov(idx-1,to,other,goal[idx]);ans[st][++sz[st]]=(state){idx,to,goal[idx]};solve(idx-1,other); }
有了这些操作,解题就很简单了,我们按照上面讲的两种情况分类,输出步数较少的方案即可
void work(int idx){if(!idx)return;if(cur[idx]==goal[idx])return work(idx-1);int other=6-cur[idx]-goal[idx];/*0:all to other, N to to*/st=0;merg(idx-1,other);ans[st][++sz[st]]=(state){idx,cur[idx],goal[idx]};solve(idx-1,other);/*1:all to to, N to other, all to from, N to to*/st=1;merg(idx-1,goal[idx]);ans[st][++sz[st]]=(state){idx,cur[idx],other};mov(idx-1,goal[idx],cur[idx],other);ans[st][++sz[st]]=(state) {idx,other,goal[idx]};solve(idx-1,cur[idx]); }
最终给出AC代码
1 #include<bits/stdc++.h> 2 using namespace std; 3 typedef long long LL; 4 const int INF=1e9+7,MAXN=50,MAXP=1e5; 5 int N,cur[MAXN],goal[MAXN],cnt; 6 char tran[]={'E','A','B','C'};/*translate*/ 7 inline void input(int *array,int opt){ 8 int ii,jj; 9 scanf("%d",&ii); 10 while(ii--){ 11 scanf("%d",&jj); 12 array[jj]=opt; 13 } 14 } 15 struct state{ int idx,from,to; }ans[2][MAXP]; 16 int st,sz[2]; 17 void mov(int idx,int from,int to,int other){ 18 /*move 1~"idx"-th to "to" 19 in a heap*/ 20 if(!idx) 21 return; 22 mov(idx-1,from,other,to); 23 ans[st][++sz[st]]=(state){idx,from,to}; 24 mov(idx-1,other,to,from); 25 } 26 void merg(int idx,int to){ 27 /*move 1~"idx"-th to "to" 28 not in a heap*/ 29 if(!idx) 30 return; 31 if(cur[idx]==to) 32 return merg(idx-1,to); 33 int other=6-cur[idx]-to; 34 merg(idx-1,other); 35 ans[st][++sz[st]]=(state){idx,cur[idx],to}; 36 mov(idx-1,other,to,cur[idx]); 37 } 38 void solve(int idx,int to){ 39 /*put 1-"idx"-th into its place*/ 40 if(!idx) 41 return; 42 if(goal[idx]==to) 43 return solve(idx-1,to); 44 int other=6-goal[idx]-to; 45 mov(idx-1,to,other,goal[idx]); 46 ans[st][++sz[st]]=(state){idx,to,goal[idx]}; 47 solve(idx-1,other); 48 } 49 void work(int idx){ 50 if(!idx) 51 return; 52 if(cur[idx]==goal[idx]) 53 return work(idx-1); 54 int other=6-cur[idx]-goal[idx]; 55 /*0:all to other, N to to*/ 56 st=0; 57 merg(idx-1,other); 58 ans[st][++sz[st]]=(state){idx,cur[idx],goal[idx]}; 59 solve(idx-1,other); 60 /*1:all to to, N to other, all to from, N to to*/ 61 st=1; 62 merg(idx-1,goal[idx]); 63 ans[st][++sz[st]]=(state){idx,cur[idx],other}; 64 mov(idx-1,goal[idx],cur[idx],other); 65 ans[st][++sz[st]]=(state) {idx,other,goal[idx]}; 66 solve(idx-1,cur[idx]); 67 } 68 int main(){ 69 scanf("%d",&N); 70 input(cur,1); 71 input(cur,2); 72 input(cur,3); 73 input(goal,1); 74 input(goal,2); 75 input(goal,3); 76 77 work(N); 78 79 for(int i=1,ed=min(sz[0],sz[1]),j=sz[0]>sz[1];i<=ed;i++) 80 printf("move %d from %c to %c\n",ans[j][i].idx,tran[ans[j][i].from],tran[ans[j][i].to]); 81 printf("%d",min(sz[0],sz[1])); 82 return 0; 83 }
View Code
转载于:https://www.cnblogs.com/guoshaoyang/p/11114054.html
洛谷 P1242 新汉诺塔相关推荐
- [dfs] 洛谷 P1242 新汉诺塔
题目描述 设有n个大小不等的中空圆盘,按从小到大的顺序从1到n编号.将这n个圆盘任意的迭套在三根立柱上,立柱的编号分别为A.B.C,这个状态称为初始状态. 现在要求找到一种步数最少的移动方案,使得从初 ...
- P1242 新汉诺塔
题目描述 设有n个大小不等的中空圆盘,按从小到大的顺序从1到n编号.将这n个圆盘任意的迭套在三根立柱上,立柱的编号分别为A.B.C,这个状态称为初始状态. 现在要求找到一种步数最少的移动方案,使得从初 ...
- 新汉诺塔(洛谷P1242)含第11个数据的解决办法
解析 应该从大到小一个个移,这样后面大盘就可以直接忽略,保证没有冗余操作,必定最优(如果先移动小的,后面移动大的时还要动小的) 对于第id个从当前位置到目标的移动有两种移动方案: 法1:把id-1个移 ...
- UVA 10795 新汉诺塔问题
https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem& ...
- 汉诺塔(河内之塔)相关题目
1. 标准汉诺塔 题目: 汉诺塔由三根柱子(分别用A.B.C表示)和n个大小互不相同的空心盘子组成.一开始n个盘子都摞在柱子A上,大的在下面,小的在上面,形成了一个塔状的锥形体. 对汉诺塔的一次合法的 ...
- 汉诺塔的递归实现,看完就懂了
对于要实现汉诺塔递归程序的同学,我相信有一部分同学还没有真正的玩过汉诺塔这个游戏,我建议先在手机应用商店下载一个汉诺塔游戏去感受一下,当了解了游戏的玩法之后,也更方便你去理解递归代码的逻辑. 下面通过 ...
- python面向过程实践汉诺塔_递归汉诺塔-和递归汉诺塔相关的内容-阿里云开发者社区...
多柱汉诺塔最优算法设计探究 多柱汉诺塔最优算法设计探究 引言 汉诺塔算法一直是算法设计科目的最具代表性的研究问题,本文关注于如何设计多柱汉诺塔最优算法的探究.最简单的汉诺塔是三个柱子(A.B.C),因 ...
- 利用递归、迭代解决斐波那契数列问题与汉诺塔难题
有人说,"普通程序员使用迭代,天才程序员使用递归",真是这样吗? 1.浅谈递归与迭代 <1>递归的基本概念: 程序调用自身的编程技巧称为递归,是函数自己调用自己. 一个 ...
- 汉诺塔(Tower of Hanoi) 递归代码实现 c语言(顺序栈实现)
文章目录 c语言简化版 C语言强化版(能看到每一步每个塔的情况)(使用了顺序栈库) main.c sequential_stack.cpp sequential_stack.h 运行结果 找了个汉诺塔 ...
最新文章
- (int)a和(int)a的区别
- WinAPI: GetForegroundWindow - 获取前台窗口的句柄
- 出人意料的生日会400字_出人意料的有效遗传方法进行特征选择
- goods.java_javaweb网上书城项目 1.用户管理:注册会员 - 下载 - 搜珍网
- linux dns 问题吗,Linux下DNS的问题
- orm2 中文文档 3.2 模型验证器
- isscalar matlab,大数的素性检测(用MATLAB仿真)
- 三层交换机配置DHCP的解决方案
- 【渝粤教育】广东开放大学 财会法规和职业道德 形成性考核 (26)
- 照片被误删?别着急,EasyRecovery帮你找回来
- SQL SERVER 2005下载(本地使用)
- 【pdanet】免流热点共享 破解pdanet
- 测试工程师如何渡过互联网寒冬
- 显卡优化以提高计算机性能,《绝地求生》完美显卡优化教程:低配电脑也能吃鸡无压力!...
- CentOS7 配置与管理DHCP服务器及DHCP中继代理 DHCP服务实训整合
- 智能机器人走迷宫c语言游戏,(动态规划)机器人走迷宫问题(示例代码)
- 如何在WPF中使用虚拟键盘
- NFC NFC手机 兼容的标签 支持Mifare Classic 手机列表
- SpringData JPA 之疑难杂症
- “携号转网”11日在全国开启试运行,申请流程看过来
热门文章
- 2013\National _C_C++_B\1.猜灯谜
- 【Linux】一步一步学Linux——exec命令(208)
- 我的世界手机版java安装失败_我的世界中国版JAVA路径错误的解决方法分享
- 用python处理文本数据_用Python读取几十万行文本数据
- html+css+javascript 网页设计 从入门到精通_绵阳美工设计学习
- java做 excel文件的 导入导出 (SSM+layer)
- ansible(6)——模块命令command、shell详细用法
- 16位汇编 写入显存B800:0000 输出字符串
- VC++ ipaddress控件的使用
- java图片转ascii字符画(转载)