原题链接

题目描述

设有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号盘子移动到闲置的位置,然后移动即可,代码也十分简单

 #include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int INF=1e9+,MAXN=;
int N,cur[MAXN],goal[MAXN],ans;
char tran[]={' ','A','B','C'};/*translate*/
inline void input(int *array,int opt){
int ii,jj;
scanf("%d",&ii);
while(ii--){
scanf("%d",&jj);
array[jj]=opt;
}
}
void dfs(int from/*from idx*/,int to/*to pos*/){/*move the "from"-th plate to position "to"*/
if(cur[from]==to)
return;
int other=-cur[from]-to;
for(int i=from-;i>=;i--)
dfs(i,other);
printf("move %d from %c to %c\n",from,tran[cur[from]],tran[to]);
cur[from]=to;
ans++;
}
int main(){
scanf("%d",&N);
input(cur,);
input(cur,);
input(cur,);
input(goal,);
input(goal,);
input(goal,); for(int i=N;i>=;i--)
dfs(i,goal[i]);
printf("%d",ans);
return ;
}

但正如上面所说的,这是一个假算法。我们认定如图的变换方法是正确的,是建立在汉诺塔的性质的基础上的。在汉诺塔中,未归位的盘子都是连续叠加的(如下图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-,from,other,to);
ans[st][++sz[st]]=(state){idx,from,to};
mov(idx-,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-,to);
int other=-cur[idx]-to;
merg(idx-,other);
ans[st][++sz[st]]=(state){idx,cur[idx],to};
mov(idx-,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-,to);
int other=-goal[idx]-to;
mov(idx-,to,other,goal[idx]);
ans[st][++sz[st]]=(state){idx,to,goal[idx]};
solve(idx-,other);
}

有了这些操作,解题就很简单了,我们按照上面讲的两种情况分类,输出步数较少的方案即可

void work(int idx){
if(!idx)
return;
if(cur[idx]==goal[idx])
return work(idx-);
int other=-cur[idx]-goal[idx];
/*0:all to other, N to to*/
st=;
merg(idx-,other);
ans[st][++sz[st]]=(state){idx,cur[idx],goal[idx]};
solve(idx-,other);
/*1:all to to, N to other, all to from, N to to*/
st=;
merg(idx-,goal[idx]);
ans[st][++sz[st]]=(state){idx,cur[idx],other};
mov(idx-,goal[idx],cur[idx],other);
ans[st][++sz[st]]=(state) {idx,other,goal[idx]};
solve(idx-,cur[idx]);
}

最终给出AC代码

 #include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int INF=1e9+,MAXN=,MAXP=1e5;
int N,cur[MAXN],goal[MAXN],cnt;
char tran[]={'E','A','B','C'};/*translate*/
inline void input(int *array,int opt){
int ii,jj;
scanf("%d",&ii);
while(ii--){
scanf("%d",&jj);
array[jj]=opt;
}
}
struct state{ int idx,from,to; }ans[][MAXP];
int st,sz[];
void mov(int idx,int from,int to,int other){
/*move 1~"idx"-th to "to"
in a heap*/
if(!idx)
return;
mov(idx-,from,other,to);
ans[st][++sz[st]]=(state){idx,from,to};
mov(idx-,other,to,from);
}
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-,to);
int other=-cur[idx]-to;
merg(idx-,other);
ans[st][++sz[st]]=(state){idx,cur[idx],to};
mov(idx-,other,to,cur[idx]);
}
void solve(int idx,int to){
/*put 1-"idx"-th into its place*/
if(!idx)
return;
if(goal[idx]==to)
return solve(idx-,to);
int other=-goal[idx]-to;
mov(idx-,to,other,goal[idx]);
ans[st][++sz[st]]=(state){idx,to,goal[idx]};
solve(idx-,other);
}
void work(int idx){
if(!idx)
return;
if(cur[idx]==goal[idx])
return work(idx-);
int other=-cur[idx]-goal[idx];
/*0:all to other, N to to*/
st=;
merg(idx-,other);
ans[st][++sz[st]]=(state){idx,cur[idx],goal[idx]};
solve(idx-,other);
/*1:all to to, N to other, all to from, N to to*/
st=;
merg(idx-,goal[idx]);
ans[st][++sz[st]]=(state){idx,cur[idx],other};
mov(idx-,goal[idx],cur[idx],other);
ans[st][++sz[st]]=(state) {idx,other,goal[idx]};
solve(idx-,cur[idx]);
}
int main(){
scanf("%d",&N);
input(cur,);
input(cur,);
input(cur,);
input(goal,);
input(goal,);
input(goal,); work(N); for(int i=,ed=min(sz[],sz[]),j=sz[]>sz[];i<=ed;i++)
printf("move %d from %c to %c\n",ans[j][i].idx,tran[ans[j][i].from],tran[ans[j][i].to]);
printf("%d",min(sz[],sz[]));
return ;
}

洛谷 P1242 新汉诺塔的更多相关文章

  1. 洛谷P1242 新汉诺塔(dfs,模拟退火)

    洛谷P1242 新汉诺塔 最开始的思路是贪心地将盘子从大到小依次从初始位置移动到目标位置. 方法和基本的汉诺塔问题的方法一样,对于盘子 \(i\) ,将盘子 \(1\to i-1\) 放置到中间柱子上 ...

  2. 洛谷P1242 新汉诺塔

    传送门啦 首先要将第n个盘子从x到y,那么就要把比n小的盘子全部移到6-x-y,然后将n移到y 仔细想想:6代表的是3根初始柱,3根目标柱. 6-(x+y) 便是我们的中转柱了,因为到这个位置是最优的 ...

  3. 洛谷P1242 新汉诺塔 【神奇的递归】

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

  4. P1242 新汉诺塔(搜索+模拟退火)

    题目链接:传送门 题目大意: 汉诺塔,给定n个盘子(n <= 45),起始状态和结束状态,求最小的步数以及路径. 思路: 考虑用dfs贪心地将剩余最大盘归位. #include<bits/ ...

  5. BZOJ1019 汉诺塔/洛谷P4285 [SHOI2008]汉诺塔

    汉诺塔(BZOJ) P4285 [SHOI2008]汉诺塔 居然是省选题,还是DP!(我的DP菜得要死,碰见就丢分) 冥思苦想了1h+ \(\to\) ?! 就是普通的hanoi NOI or HNO ...

  6. P1242 新汉诺塔(hanio)

    这道题加深了hanio的理解 如果我们要移动第n个盘子.那么就是说,n+1以后(包括n+1)的盘子都已经到位了 #include<iostream> #include<cstdio& ...

  7. P1242 新汉诺塔

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

  8. 大白_uva10795_新汉诺塔

    题意:给出所有盘子的初态和终态,问最少多少步能从初态走到终态,其余规则和老汉诺塔一样. 思路: 若要把当前最大的盘子m从1移动到3,那么首先必须把剩下的所有盘子1~m-1放到2上,然后把m放到3上. ...

  9. UVA 10795 新汉诺塔问题

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

随机推荐

  1. 剑指offer——03替换空格

    题目描述 请实现一个函数,将一个字符串中的每个空格替换成“%20”.例如,当字符串为We Are Happy.则经过替换之后的字符串为We%20Are%20Happy.   注意事项: <剑指o ...

  2. Spring Boot 文件下载

    1. 文件下载类 import javax.servlet.http.HttpServletResponse; import java.io.*; public class DownloadUtil ...

  3. ES6 学习 -- 字符串新增方法

    1.检测字符串中是否包含某个字符 ES5方法:string.indexOf("需要检测的字符"),如果返回值为-1,则说明当前字符串中不存在这个字符,返回值不为-1,则 是当前字符 ...

  4. Apache2.2+tomcat7 负载均衡配置

    思路及步骤:第一步配置tomcat,第二步配置apache 服务器,第三步添加项目到tomcat中并测试 第一步配置tomcat 1,打开 第一个tomcat,conf文件夹下的server.xml ...

  5. 添加ASP.NET AJAX控件工具集到VS2010的方法

    在VS2010中Ajax控件只有5个,其实还有很多支持AJAX特定功能的服务器控件,微软是将这些控件当作开放源代码项目.所以没有集成到VS2010中.这些AJAX控件被称为ASP.NET AJAX控件 ...

  6. [NOIP2005] 过河【Dp,思维题,缩点】

    Online Judge:Luogu P1052 Label:Dp,思维题,缩点,数学 题目描述 在河上有一座独木桥,一只青蛙想沿着独木桥从河的一侧跳到另一侧.在桥上有一些石子,青蛙很讨厌踩在这些石子 ...

  7. ransformResourcesWithMergeJavaResForDebug问题

    错误内容: Error:Execution failed for task ':app:transformResourcesWithMergeJavaResForDebug'. > java.i ...

  8. soj116 快乐串

    题意:定义一个串是k-happy的:对于所有的Ai,都有Aj(j!=i),使得|Ai-Aj|<=k. 问使得原串至少存在一个长度>=m的连续子串是k-happy的最小的k? 标程: #in ...

  9. chrome的驱动安装

    首先找到对应的chromedriver,百度搜索,http://npm.taobao.org/mirrors/chromedriver/ 将下载好的chrome驱动解压,放在/usr/loacl/bi ...

  10. requestAnimationFrame动画封装

    function Animator(duration, progress) { this.duration = duration; this.progress = progress; this.nex ...