汉诺塔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盘. 则是正确的.
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,
后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;
}