新汉诺塔

Description

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

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

移动时有如下要求:

  • 一次只能移一个盘;
  • 不允许把大盘移到小盘上面。

Analysis

没有头绪的一题,如果圆盘有序放置,是不是题目会简单很多?答案是肯定的。

显而易见,每次只能移动顶层的圆盘,但是此思维类似于爆搜,是不可取的。回到汉诺塔游戏的思路,将n-1个盘子转移走,把自己放到目标杆子上,把n-1个盘子放回来,所以是一个递归的思想。

这题规则有所改变,每个盘子都有自己的目标状态。只能把目标状态中底部的盘子先达到目标,再把另外n-1个盘子弄上去。而瞄准一个盘子怎么把它放上去呢?

首先肯定要把目标点的盘子拾搬,把自己头上的拾搬,最后才能把自己放上去。这显然是正确的,但是拾搬的扔到哪呢?只能扔到另一根柱子上了。

接着,我就没有彻底没有头绪了,因为任意迭套存在小的上面有大环的情况,这个拾搬的策略实在是太复杂了。先写一个初始状态一定为有序的code试试看。

懵逼,竟然AC了。原来是我题目理解错了,放置的时候一定是有序的...

那么这个题目就比较简单了,每次把需要移动的最大的移动过去,为了给他腾出地方,把比他小的盘子中占据目标位置和在他头上的都扔另一个杆子上去,产生子问题。

Code

#include <bits/stdc++.h>
const int N=51;
const char M[3]={'A','B','C'};
int pole[N],tar[N],step;
void move(int x,int s,int t){
	if(t==s)return;
	for(int i=x-1;i;i--)
		if(pole[i]==t)move(i,t,3-s-t);
		else if(pole[i]==s)move(i,s,3-s-t);
	pole[x]=t;
	step++;
	printf("move %d from %c to %c\n",x,M[s],M[t]);
}
int main(){
	freopen("test.in","r",stdin);
	freopen("test.out","w",stdout);
	int n;
	scanf("%d",&n);
	for(int i=0;i<2;i++)
		for(int j=0;j<3;j++){
			int num,p;
			scanf("%d",&num);
			for(int k=1;k<=num;k++){
				scanf("%d",&p);
				if(!i)pole[p]=j;
				if(i)tar[p]=j;
			}
		}
	for(int i=n;i>=1;i--)
		move(i,pole[i],tar[i]);
	printf("%d\n",step);
	return 0;
}
posted @ 2018-08-21 08:24  Srzer  阅读(896)  评论(0编辑  收藏  举报