HDOJ 1997 汉诺塔VII (递归模拟 or 规律解法)



汉诺塔VII

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 1358    Accepted Submission(s): 891


Problem Description
n个盘子的汉诺塔问题的最少移动次数是2^n-1,即在移动过程中会产生2^n个系列。由于发生错移产生的系列就增加了,这种错误是放错了柱子,并不会把大盘放到小盘上,即各柱子从下往上的大小仍保持如下关系 :
n=m+p+q
a1>a2>...>am
b1>b2>...>bp
c1>c2>...>cq
ai是A柱上的盘的盘号系列,bi是B柱上的盘的盘号系列, ci是C柱上的盘的盘号系列,最初目标是将A柱上的n个盘子移到C盘. 给出1个系列,判断它是否是在正确的移动中产生的系列.
例1:n=3
3
2
1
是正确的
例2:n=3
3
1
2
是不正确的。
注:对于例2如果目标是将A柱上的n个盘子移到B盘. 则是正确的.
 

Input
包含多组数据,首先输入T,表示有T组数据.每组数据4行,第1行N是盘子的数目N<=64.
后3行如下
m a1 a2 ...am
p b1 b2 ...bp
q c1 c2 ...cq
N=m+p+q,0<=m<=N,0<=p<=N,0<=q<=N,
 

Output
对于每组数据,判断它是否是在正确的移动中产生的系列.正确输出true,否则false
 

Sample Input
  
  
6 3 1 3 1 2 1 1 3 1 3 1 1 1 2 6 3 6 5 4 1 1 2 3 2 6 3 6 5 4 2 3 2 1 1 3 1 3 1 2 1 1 20 2 20 17 2 19 18 16 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1
 

Sample Output
  
  
true false false false true true
 

题解:题目很好的,不熟悉汉诺塔移动的规律的同学,请进,传送门:普普的博客

有两种解法。

一,递归模拟法:
用递归模拟汉诺塔移动的过程,判断给出的状态是否能全部移到第三根柱子上。

①考虑最大盘子 n 号盘子,移动方向为A——>C,它只能在A或者C上,如果它在B上,则为false;

②如果 n 号盘子在 A 上,则其上的 n-1 号盘子必处于从A——>B的移动过程中,此时最大盘号为 n-1,移动方向为A—>B;

③如果 n 号盘子在 C 上,则其上的 n-1 号盘子必处于从B——>C的移动过程中,此时最大盘号为 n-1,移动方向为B—>C;

④重复①②③。

代码如下:

#include<cstdio>
#include<cstring>
using namespace std;

void input(int n,int *a)
{
	int i;
	memset(a,0,65*sizeof(int));//这里不能写成sizeof(a) 
	for(i=0;i<n;++i)
		scanf("%d",&a[i]);
}

bool move(int n,int *a,int *b,int *c)//模拟汉诺塔移动过程 
{
	if(n==0) 
		return true;
	if(n==a[0])//如果第n个盘子在A上,则将n-1个盘子(除去n之外的盘子)借助C柱移到B柱上面 
		return move(n-1,++a,c,b);
	else if(n==c[0])//如果第n个盘子在C上面,则将n-1盘子从B柱(除去n之外的盘子)借助A柱移到C柱上面 
		return move(n-1,b,a,++c);
	return false;
}

int main()
{
	int t,i,m,q,p,n;
	int a[65],b[65],c[65];
	scanf("%d",&t);
	while(t--)
	{
		scanf("%d",&n);
		scanf("%d",&m);
		input(m,a);
		scanf("%d",&q);
		input(q,b);
		scanf("%d",&p);
		input(p,c);
		if(move(n,a,b,c))
			printf("true\n");
		else
			printf("false\n");
	}
	return 0;
} 


规律解法:

通过上面汉诺塔模型移动的过程,我们可以发现一些规律。

①盘为奇数时,最底为偶数的只能在2号柱,底数为奇数的盘只能在1,3号柱

②偶数与上面相反

③每一根柱子上的盘是奇偶互换的

代码如下:


#include<cstdio>
#include<cstring>
#define maxn 65
using namespace std;
int a[3][maxn],n;

bool solve()
{
	int i,j;
	for(i=0;i<3;++i)
	{
		if(a[i][0]==0)
			continue;
		if((n+i+a[i][1])%2)//盘的个数为奇数时,第二个柱子最下面的盘为偶数,底数为奇数的指挥出现在1,3,柱上;盘数为偶数时则相反 
			return false;
		for(j=2;j<=a[i][0];++j)//判断前后是否奇偶性相反 
		{
			if((a[i][j]+a[i][j-1])%2==0)
				return false;
		}
	}
	return true;
}

int main()
{
	int i,t;
	scanf("%d",&t);
	while(t--)
	{
		scanf("%d",&n);
		for(i=0;i<3;++i)
		{
			scanf("%d",&a[i][0]);
			for(int j=1;j<=a[i][0];++j)
				scanf("%d",&a[i][j]);
		}
		if(solve())
			printf("true\n");
		else
			printf("false\n");
	}
	return 0;
} 




  • 5
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值