洛谷 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号盘子移动到闲置的位置,然后移动即可,代码也十分简单
#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的过程又怎么实现呢?我们需要三种操作
- 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-,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 新汉诺塔的更多相关文章
- 洛谷P1242 新汉诺塔(dfs,模拟退火)
洛谷P1242 新汉诺塔 最开始的思路是贪心地将盘子从大到小依次从初始位置移动到目标位置. 方法和基本的汉诺塔问题的方法一样,对于盘子 \(i\) ,将盘子 \(1\to i-1\) 放置到中间柱子上 ...
- 洛谷P1242 新汉诺塔
传送门啦 首先要将第n个盘子从x到y,那么就要把比n小的盘子全部移到6-x-y,然后将n移到y 仔细想想:6代表的是3根初始柱,3根目标柱. 6-(x+y) 便是我们的中转柱了,因为到这个位置是最优的 ...
- 洛谷P1242 新汉诺塔 【神奇的递归】
题目描述 设有n个大小不等的中空圆盘,按从小到大的顺序从1到n编号.将这n个圆盘任意的迭套在三根立柱上,立柱的编号分别为A.B.C,这个状态称为初始状态. 现在要求找到一种步数最少的移动方案,使得从初 ...
- P1242 新汉诺塔(搜索+模拟退火)
题目链接:传送门 题目大意: 汉诺塔,给定n个盘子(n <= 45),起始状态和结束状态,求最小的步数以及路径. 思路: 考虑用dfs贪心地将剩余最大盘归位. #include<bits/ ...
- BZOJ1019 汉诺塔/洛谷P4285 [SHOI2008]汉诺塔
汉诺塔(BZOJ) P4285 [SHOI2008]汉诺塔 居然是省选题,还是DP!(我的DP菜得要死,碰见就丢分) 冥思苦想了1h+ \(\to\) ?! 就是普通的hanoi NOI or HNO ...
- P1242 新汉诺塔(hanio)
这道题加深了hanio的理解 如果我们要移动第n个盘子.那么就是说,n+1以后(包括n+1)的盘子都已经到位了 #include<iostream> #include<cstdio& ...
- P1242 新汉诺塔
题目描述 设有n个大小不等的中空圆盘,按从小到大的顺序从1到n编号.将这n个圆盘任意的迭套在三根立柱上,立柱的编号分别为A.B.C,这个状态称为初始状态. 现在要求找到一种步数最少的移动方案,使得从初 ...
- 大白_uva10795_新汉诺塔
题意:给出所有盘子的初态和终态,问最少多少步能从初态走到终态,其余规则和老汉诺塔一样. 思路: 若要把当前最大的盘子m从1移动到3,那么首先必须把剩下的所有盘子1~m-1放到2上,然后把m放到3上. ...
- UVA 10795 新汉诺塔问题
https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem& ...
随机推荐
- 如何把本地文件上传github
1.$ git config --global user.name "xxx" 2.$ git config --global user.email xxx@qq.com 3.进入 ...
- oracle 管理表空间
表空间:是oracle数据库中最大的逻辑存储结构,与操作系统中的数据文件相对应,用于存储数据库中用户创建的所有内容 表空间>数据文件 4.1创建一个永久性表空间myspace create ta ...
- 使用CSS为图片添加更多趣味的5种方法
使用Photoshop为每个图片添加某种样式虽然可行,但会是相当乏味且困难的长久工作.下面要介绍的CSS技巧将帮助你从痛苦中解脱出来! 阴影效果 通过使用带有一些padding之的背景图来添加阴影效果 ...
- 2019-7-29-C#-在基类定义好方法让子类继承接口就能实现
title author date CreateTime categories C# 在基类定义好方法让子类继承接口就能实现 lindexi 2019-07-29 09:57:49 +0800 201 ...
- 【数论】[SDOI2010]古代猪文
大概就是求这个: $$G^\sum_{k|N} C_{n}^{k}$$ 显然只要把后面的$\sum_{k|N}C_{n}^{k}$求出来就好了 几个要用的定理: 欧拉定理的推论:(a和n互质) $$a ...
- CSS3——过渡
过渡(transition)是CSS3中具有颠覆性的特征之一,我们可以在不使用 Flash 动画或 JavaScript 的情况下,当元素从一种样式变换为另一种样式时为元素添加效果. 帧动画:通过一帧 ...
- RocketMQ在linux下安装部署
本博客以当前RocketMQ最新版介绍:v4.4.0 环境要求 64位JDK 1.8+; Maven 3.2.x; // 源码编译时需要用到 二进制文件安装 下载二进制文件:http://mirror ...
- vue+Iview+gulp 生成文档说明
1.安装npm gulp相关插件 比如:gulp.gulp-concat.gulp-htmlmin.gulp-cssmin.gulp-cheerio.gulp-clean 2. 编写gulpfile. ...
- 多线程编程9认识等待函数WaitForSingleObject
一下子跳到等待函数 WaitForSingleObject, 是因为下面的 Mutex.Semaphore.Event.WaitableTimer 等同步手段都要使用这个函数; 不过等待函数可不止 W ...
- MFC int转CString
方法一. int iVar = 7489; char szBuf[100]; _itoa(iVar, szBuf, 10); CString ss(szBuf); SetDlgItemText(IDC ...