P1242 新汉诺塔
题目描述
设有n个大小不等的中空圆盘,按从小到大的顺序从1到n编号。将这n个圆盘任意的迭套在三根立柱上,立柱的编号分别为A、B、C,这个状态称为初始状态。
现在要求找到一种步数最少的移动方案,使得从初始状态转变为目标状态。
移动时有如下要求:
·一次只能移一个盘;
·不允许把大盘移到小盘上面。
输入输出格式
输入格式:
文件第一行是状态中圆盘总数;
第二到第四行分别是初始状态中A、B、C柱上圆盘的个数和从上到下每个圆盘的编号;
第五到第七行分别是目标状态中A、B、C柱上圆盘的个数和从上到下每个圆盘的编号。
输出格式:
每行一步移动方案,格式为:move I from P to Q
最后一行输出最少的步数。
思路:
只有三个柱子,每次把一个柱子上面的圆盘都移到除该柱子和目标柱外的另一个柱子,
当然,要从最大的一个圆盘开始移动。
代码解释:
#include<iostream>
using namespace std;
int from[55],to[55],ans,n,t,m;
void dfs(int x,int y)//x代表第x大的圆盘,y代表移到第y个柱子上
{
if(from[x]==y)return ;//如果圆盘已经在目标位置上了,就不需要再移动了
for(int l=x-1;l>0;l--)//也要从大到小移动
dfs(l,6-from[x]-y);
cout<<"move"<<' '<<x<<' '<<"from"<<' '<<char(from[x]+'A'-1)<<' '<<"to"<<' '<<char(y+'A'-1)<<endl;
from[x]=y;ans++; //于是更新第x大的圆盘的位置
}
int main()
{
cin>>m;
cin>>n;for(int i=1;i<=n;i++)cin>>t,from[t]=1;
cin>>n;for(int i=1;i<=n;i++)cin>>t,from[t]=2;
cin>>n;for(int i=1;i<=n;i++)cin>>t,from[t]=3;
cin>>n;for(int i=1;i<=n;i++)cin>>t,to[t]=1;
cin>>n;for(int i=1;i<=n;i++)cin>>t,to[t]=2;
cin>>n;for(int i=1;i<=n;i++)cin>>t,to[t]=3;//夸张的读入QAQ
for(int i=m;i>0;i--)//从最大的柱子开始移动,毕竟大柱子要放在底下
dfs(i,to[i]);
cout<<ans;
return 0;
}
PS:
有这样一组数据:
3
1 3
0
2 2 1
2 2 1
0
1 3
按照曾经AC的程序,面对这样的数据,我们会先努力把圆盘 3 移动到 C 柱上。
所以我们的代码先移动了 1,2两个圆盘到 B柱,再把 3 这个圆盘从 A干脆利落地移动到 C ……
move 1 from C to A
move 2 from C to B
move 1 from A to B
move 3 from A to C
move 1 from B to C
move 2 from B to A
move 1 from C to A
7
然而以上不是最优解。下面才是:
move 3 from A to B
move 1 from C to B
move 2 from C to A
move 1 from B to A
move 3 from B to C
5
可见我们的代码忽略了空柱的作用,误以为“必须一步把当前要移动的最大圆盘移动到目标柱子”(比如需要移动把圆盘 3从 A移到 C ,我们想尽办法把 A 和 C 两个柱子上小于 3的圆盘都移到 B )。
原因是,我们的代码已知 3是最大的圆盘,也就是其它圆盘都比它小。如果先移动它到没有清空的柱子上,我们担心它会压到小盘上,这样就不符合规则了。而在最优解中,他先把圆盘 3移动到 B 柱(因为 B 是空柱,所以我们白白担心了)。这样,接下来移动 1,2两个圆盘时就直接到了它们的目的地: A柱。
我现在除了特判还没想到别的:
if(m==3&&n==1&&to[3]==3)
{
cout<<"move 3 from A to B"<<endl;
cout<<"move 1 from C to B"<<endl;
cout<<"move 2 from C to A"<<endl;
cout<<"move 1 from B to A"<<endl;
cout<<"move 3 from B to C"<<endl;
cout<<5;
}