原题链接

题目描述

设有n个大小不等的中空圆盘,按从小到大的顺序从1到n编号。将这n个圆盘任意的迭套在三根立柱上,立柱的编号分别为A、B、C,这个状态称为初始状态。

现在要求找到一种步数最少的移动方案,使得从初始状态转变为目标状态。

移动时有如下要求:

·一次只能移一个盘;

·不允许把大盘移到小盘上面。

输入输出格式

输入格式:

文件第一行是状态中圆盘总数;

第二到第四行分别是初始状态中A、B、C柱上圆盘的个数和从上到下每个圆盘的编号;

第五到第七行分别是目标状态中A、B、C柱上圆盘的个数和从上到下每个圆盘的编号。

输出格式:

每行一步移动方案,格式为:move I from P to Q

最后一行输出最少的步数。

输入输出样例

输入样例#1:

5
3 3 2 1
2 5 4
0
1 2
3 5 4 3
1 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的过程又怎么实现呢?我们需要三种操作

  1. move(idx,from,to):当1~idx在同一个桩子上时,将它们移到to号桩子
  2. merge(idx,to):当1~idx不在同一个桩子上时,将它们移到to号桩子
  3. 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 新汉诺塔相关推荐

  1. [dfs] 洛谷 P1242 新汉诺塔

    题目描述 设有n个大小不等的中空圆盘,按从小到大的顺序从1到n编号.将这n个圆盘任意的迭套在三根立柱上,立柱的编号分别为A.B.C,这个状态称为初始状态. 现在要求找到一种步数最少的移动方案,使得从初 ...

  2. P1242 新汉诺塔

    题目描述 设有n个大小不等的中空圆盘,按从小到大的顺序从1到n编号.将这n个圆盘任意的迭套在三根立柱上,立柱的编号分别为A.B.C,这个状态称为初始状态. 现在要求找到一种步数最少的移动方案,使得从初 ...

  3. 新汉诺塔(洛谷P1242)含第11个数据的解决办法

    解析 应该从大到小一个个移,这样后面大盘就可以直接忽略,保证没有冗余操作,必定最优(如果先移动小的,后面移动大的时还要动小的) 对于第id个从当前位置到目标的移动有两种移动方案: 法1:把id-1个移 ...

  4. UVA 10795 新汉诺塔问题

    https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem& ...

  5. 汉诺塔(河内之塔)相关题目

    1. 标准汉诺塔 题目: 汉诺塔由三根柱子(分别用A.B.C表示)和n个大小互不相同的空心盘子组成.一开始n个盘子都摞在柱子A上,大的在下面,小的在上面,形成了一个塔状的锥形体. 对汉诺塔的一次合法的 ...

  6. 汉诺塔的递归实现,看完就懂了

    对于要实现汉诺塔递归程序的同学,我相信有一部分同学还没有真正的玩过汉诺塔这个游戏,我建议先在手机应用商店下载一个汉诺塔游戏去感受一下,当了解了游戏的玩法之后,也更方便你去理解递归代码的逻辑. 下面通过 ...

  7. python面向过程实践汉诺塔_递归汉诺塔-和递归汉诺塔相关的内容-阿里云开发者社区...

    多柱汉诺塔最优算法设计探究 多柱汉诺塔最优算法设计探究 引言 汉诺塔算法一直是算法设计科目的最具代表性的研究问题,本文关注于如何设计多柱汉诺塔最优算法的探究.最简单的汉诺塔是三个柱子(A.B.C),因 ...

  8. 利用递归、迭代解决斐波那契数列问题与汉诺塔难题

    有人说,"普通程序员使用迭代,天才程序员使用递归",真是这样吗? 1.浅谈递归与迭代 <1>递归的基本概念: 程序调用自身的编程技巧称为递归,是函数自己调用自己. 一个 ...

  9. 汉诺塔(Tower of Hanoi) 递归代码实现 c语言(顺序栈实现)

    文章目录 c语言简化版 C语言强化版(能看到每一步每个塔的情况)(使用了顺序栈库) main.c sequential_stack.cpp sequential_stack.h 运行结果 找了个汉诺塔 ...

最新文章

  1. (int)a和(int)a的区别
  2. WinAPI: GetForegroundWindow - 获取前台窗口的句柄
  3. 出人意料的生日会400字_出人意料的有效遗传方法进行特征选择
  4. goods.java_javaweb网上书城项目 1.用户管理:注册会员 - 下载 - 搜珍网
  5. linux dns 问题吗,Linux下DNS的问题
  6. orm2 中文文档 3.2 模型验证器
  7. isscalar matlab,大数的素性检测(用MATLAB仿真)
  8. 三层交换机配置DHCP的解决方案
  9. 【渝粤教育】广东开放大学 财会法规和职业道德 形成性考核 (26)
  10. 照片被误删?别着急,EasyRecovery帮你找回来
  11. SQL SERVER 2005下载(本地使用)
  12. 【pdanet】免流热点共享 破解pdanet
  13. 测试工程师如何渡过互联网寒冬
  14. 显卡优化以提高计算机性能,《绝地求生》完美显卡优化教程:低配电脑也能吃鸡无压力!...
  15. CentOS7 配置与管理DHCP服务器及DHCP中继代理 DHCP服务实训整合
  16. 智能机器人走迷宫c语言游戏,(动态规划)机器人走迷宫问题(示例代码)
  17. 如何在WPF中使用虚拟键盘
  18. NFC NFC手机 兼容的标签 支持Mifare Classic 手机列表
  19. SpringData JPA 之疑难杂症
  20. “携号转网”11日在全国开启试运行,申请流程看过来

热门文章

  1. 2013\National _C_C++_B\1.猜灯谜
  2. 【Linux】一步一步学Linux——exec命令(208)
  3. 我的世界手机版java安装失败_我的世界中国版JAVA路径错误的解决方法分享
  4. 用python处理文本数据_用Python读取几十万行文本数据
  5. html+css+javascript 网页设计 从入门到精通_绵阳美工设计学习
  6. java做 excel文件的 导入导出 (SSM+layer)
  7. ansible(6)——模块命令command、shell详细用法
  8. 16位汇编 写入显存B800:0000 输出字符串
  9. VC++ ipaddress控件的使用
  10. java图片转ascii字符画(转载)