2022苏州市小学信息学奥赛T2-汉诺塔

题目描述

汉诺塔问题是源于印度一个古老传说的益智玩具。大梵天创造世 界的时候做了三根金刚石柱子,在一根柱子上从上往下按照从小到小 大顺序摞着 64 片黄金圆盘。大梵天命令婆罗门把圆盘从上面开始按 从小到大顺序重新摆放在另一根柱子上。并且规定,在小圆盘上不能 放大圆盘,在三根柱子之间一次只能移动一个圆盘。 下图是圆盘个数 n=3 时的汉诺塔,这三根柱子从左向右依次编号为 A,B,C,开始 3 个圆盘都是在 A 柱上,从上往下依次编号为 1,2,3。 最后目标是把这三个圆盘都移动到 C 柱,我们知道最少移动步数是 7步。
2022苏州市小学信息学奥赛T2-汉诺塔_第1张图片
第 1 步:1 号圆盘从 A 移动到 C

第 2 步:2 号圆盘从 A 移动到 B

第 3 步:1 号圆盘从 C 移动到 B

第 4 步:3 号圆盘从 A 移动到 C

第 5 步:1 号圆盘从 B 移动到 A

第 6 步:2 号圆盘从 B 移动到 C

第 7 步:1 号圆盘从 A 移动到 C 更一般的,如果是 n 个圆盘,最少移动步数是 2^n2
n
-1。 现在你的任务是:告诉你圆盘数 n(这些盘初始都在 A 柱上,而且从上往下编号 1 到 n),问你把这 n 个圆盘移动到 C 柱上,在最少步数移动过程中第 x 步移动的圆盘的编号,以及它从哪个柱子移动到哪个柱子?

输入

总共两行,第一行 1 个整数 n。 第二行 1 个整数 x。

输出

总共两行,第一行 1 个整数,表示第 x 步移动圆盘的编号。 第二行两个字母,中间有一个空格,表示第 x 步是从第一个字母的柱字移到第二个字母的柱子

样例

样例1
输入

3
6

输出

2
B C

样例2
输入

45 12345678910

输出

2
C A

提示
对于 20%的数据:1≤n≤16; 对于 50%的数据:1≤n≤32; 对于 100%的数据:1≤n≤64,1≤x≤2^n -1

题解

这道题先看数据量就知道肯定要用高精度,并且不能用普通方法,要找规律(至于为什么这样说后面我会说的)

朴素方法

#include
using namespace std;
long long n,m,sum=0;
char a,b,c;
void f(int n,char a,char c,char b)
{
	if(n>0)
	{
	f(n-1,a,b,c);
	sum++;
	if(sum==m)
	{
	printf("%d\n",n);
	printf("%c %c",a,c);
    }
	f(n-1,b,c,a);
    }
}
int main()
{
	cin>>n;
	cin>>m;
	a='A';
	b='B';
	c='C';
	f(n,a,c,b);
}

long long的长度为2^32-1 跟题目的2^64-1相差甚远,所以内存就会炸。因为它是递归,又因为数据量太大所以还会超时

AC方法

既然要找规律,那么我们就先写一个打表的代码

#include
#include
using namespace std;
long long n,sum=0;
char a,b,c;
void f(long long n,char a,char c,char b)
{
	if(n>0)
	{
	f(n-1,a,b,c);
	sum++;
	cout<<"第"<<sum<<"步"<<" "<<n<<"  "<<a<<c<<"    ";
	if(sum%3==0)
	{
		cout<<endl;
	}
	f(n-1,b,c,a);
    }
}
int main()
{
	cin>>n;
	a='A';
	b='B';
	c='C';
	f(n,a,c,b);
}

以下为打表出来的结果
2022苏州市小学信息学奥赛T2-汉诺塔_第2张图片
2022苏州市小学信息学奥赛T2-汉诺塔_第3张图片
由这两幅图我们不难发现数字与字母的规律

数字规律

#include
using namespace std;
int main()
{
	long long n,m;
	cin>>n>>m;
	if(m%2==1)
	cout<<1<<endl;
	else
	{
		long long k=1;
	    long long p=m;
		while(p%2==0&&p!=0)
		{
			p/=2;
			k++;
		}
		cout<<k<<endl;
	}
	return 0;
 } 

这里并没有用高精度,在源代码上会说明

字母规律

if(n%2==0)
	{
	if(check1(3)==0)
	{
		if(temp%2==0)
		{
			cout<<"C B";
			return 0;
		}
		else
		{
			cout<<"B C";
			return 0;
		}
	}
	else
	if(check1(3)==1)
	{
		if(temp%2==0)
		{
		cout<<"B A";
		return 0;
	    }
	    else
	    {
	    	cout<<"A B";
	    	return 0;
		}
	}
	else
	if(check1(3)==2)
	{
		if(temp%2==0)
		{
		cout<<"A C";
		return 0;
	    }
	    else
	    {
	    	cout<<"C A";
	    	return 0;
		}
	}
    }
    else
    {
    if(check1(3)==0)
	{
	if(temp%2==0)
	{
	cout<<"B C";
	return 0;
	}
	else
	{
	cout<<"C B";
	return 0;
	}
	}
	else
	if(check1(3)==1)
	{
	if(temp%2==0)
	{
	cout<<"C A";
	return 0;
	}
	else
	{
	cout<<"A C";
	return 0;
	}
	}
	else
	if(check1(3)==2)
	{
	if(temp%2==0)
	{
	cout<<"A B";
	return 0;
	}
	else
	{
	cout<<"B A";
	return 0;
	}
	}
	}

n为圆盘数量,temp储存的是当前移动圆盘的编号,check1为高精度取余函数

有了上述的规律那么我们就可以完成这道题目了

#include
using namespace std;
int n,m=0,k=1,num=1;//k为当前移动圆盘的编号 
char a[22],e[22];
int b[22],c[22];
long long check(int n)//这里为一个特殊的高精度取余 
{
	long long ans=0;
	for(int i=num;i<=m;i++)
	ans=(ans*10+(a[i]-'0'))%n;
	return ans;
}
long long check1(int x)//普通的高精度取余 
{
	long long sum=0;
	for(int i=1;i<=m;i++)
	sum=(sum*10+(e[i]-'0'))%x;
	return sum;
}
int main()
{
	cin>>n;
	while(cin>>a[++m]);//为防止步数为高精度数所以用char数组输入 
	m--;//为数组的长度 
	for(int i=1;i<=m;i++)//将数据转换成int形(下面有用) 
	b[i]=a[i]-'0';
	for(int i=1;i<=m;i++)//用e[i]备份一下 
	e[i]=a[i];
	//数字规律 
	if(a[m]=='1'||a[m]=='3'||a[m]=='5'||a[m]=='7'||a[m]=='9')//由上面的找到的规律可以得到 
	cout<<1<<endl;
	else
	{
		while(check(2)==0)//根据规律这里的数字要不断变化,所以专门要写一个高精度取余 
		{
		//高精度除法 
		long long f=0;
		for(int i=num;i<=m;i++)
		{
		c[i]=(f*10+b[i])/2;
		f=(f*10+b[i])%2;
		}
		long long len=num;
		while(c[len]==0&&len<m)
		len++;
		num=len;//因为数字是不断变化的,所以数组的数要不断后移,因此要用num来储存len 
		for(int i=len;i<=m;i++)
		{
		a[i]=c[i]+'0';
		b[i]=c[i];
	    }
		k++;
		}
		cout<<k<<endl;
	}
	//字母规律 
	if(n%2==0)
	{
	if(check1(3)==0)
	{
		if(k%2==0)
		{
			cout<<"C B";
			return 0;
		}
		else
		{
			cout<<"B C";
			return 0;
		}
	}
	else
	if(check1(3)==1)
	{
		if(k%2==0)
		{
		cout<<"B A";
		return 0;
	    }
	    else
	    {
	    	cout<<"A B";
	    	return 0;
		}
	}
	else
	if(check1(3)==2)
	{
		if(k%2==0)
		{
		cout<<"A C";
		return 0;
	    }
	    else
	    {
	    	cout<<"C A";
	    	return 0;
		}
	}
    }
    else
    {
    if(check1(3)==0)
	{
	if(k%2==0)
	{
	cout<<"B C";
	return 0;
	}
	else
	{
	cout<<"C B";
	return 0;
	}
	}
	else
	if(check1(3)==1)
	{
	if(k%2==0)
	{
	cout<<"C A";
	return 0;
	}
	else
	{
	cout<<"A C";
	return 0;
	}
	}
	else
	if(check1(3)==2)
	{
	if(k%2==0)
	{
	cout<<"A B";
	return 0;
	}
	else
	{
	cout<<"B A";
	return 0;
	}
	}
	}
} 

你可能感兴趣的:(算法,c++,数据结构)