原题链接

题目描述

设有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号桩

洛谷 P1242 新汉诺塔洛谷 P1242 新汉诺塔

洛谷 P1242 新汉诺塔洛谷 P1242 新汉诺塔

洛谷 P1242 新汉诺塔

 

实现这一过程很简单,只要每次将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

相关文章: