汉诺塔问题
思路:
移动过程只有三步
1.A移动N-1个盘到B: F(n-1);
2.A移动最大盘到C: F(1)即为1;
3.B移动N-1个盘到C: F(n-1);
移动次数的公式F(n)=F(n-1)*2+1
#include <iostream>
#include <cstdio>
using namespace std;
void slove(int n,char a,char b,char c)///有n个塔层由a经过b的中转运送到c
{
if(n==1)///只有一个的时候,直接将a上的放到c上
{
printf("%c-%d-%c\n",a,1,c);
return;
}
else///将n-1层塔由a到b,当最后一个(第n个)运到c时,再由b运到c
{
slove(n-1,a,c,b);///有n-1个塔层由a经过c的中转运送到b
printf("%c-%d-%c\n",a,n,c);///把a中的1层塔移动到c
slove(n-1,b,a,c);///有n-1个塔层由b经过a的中转运送到c
}
}
int main()
{
int n;
cin>>n;
slove(n,'A','B','C');
return 0;
}
问题 P: 【递归与递推】Hanoi双塔问题
题目:
给定A,B,C三根足够长的细柱,在A柱上放有2n个中间有空的圆盘,共有n个不同的尺寸,每个尺寸都有两个相同的圆盘,注意这两个圆盘是不加区分的(下图为n=3的情形)。现要将 这些国盘移到C柱上,在移动过程中可放在B柱上暂存。要求:
(1)每次只能移动一个圆盘;
(2) A、B、C三根细柱上的圆盘都要保持上小下大的顺序;
任务:设An为2n个圆盘完成上述任务所需的最少移动次数,对于输入的n,输出An。
输入
输入一个正整数n,表示在A柱上放有2n个圆盘。
输出
输出一个正整数,为完成上述任务所需的最少移动次数An。
思路:
F(n)=F(n-1)*2+2
#include <iostream>
#include <string>
#include <algorithm>
using namespace std;
string ans[205];
string add(string a,string b)///高精度加法
{
string c;
reverse(a.begin(),a.end());
reverse(b.begin(),b.end());
int carry=0,temp;
for(int i=0;i<a.size()||i<b.size();i++)
{
temp=carry;
if(i<a.size()) temp+=a[i]-'0';
if(i<b.size()) temp+=b[i]-'0';
c.push_back(char(temp%10+'0'));
carry=temp/10;
}
if(carry!=0) c.push_back(char('0'+carry));
reverse(c.begin(),c.end());
int i;
for(i=0;c[i]=='0'&&i<c.size()-1;i++);///消除前导0,但保证至少有一个
c=c.substr(i,c.size()-i);
return c;
}
string multiply(string a,string b)
{
reverse(a.begin(),a.end());
reverse(b.begin(),b.end());
int c[10005]={
0};
string ans;
for(int i=0;i<a.size();i++)///注意下标都是从0开始
{
for(int j=0;j<b.size();j++)
{
///i+j为两个数相乘完的那个数所在位置,例如一个数的第二位×另一个数的第二位,所得数在结果的第四位
c[i+j]=c[i+j]+(a[i]-'0')*(b[j]-'0');///算法核心,不进位乘法
}
}
int temp,carry=0;///该位数的和temp,进位carry,与加法类似
for(int i=0;i<a.size()+b.size();i++)
{
temp=c[i]+carry;
c[i]=temp%10;
carry=temp/10;
}
for(int i=0;i<a.size()+b.size();i++) ans.push_back(char('0'+c[i]));
int i;
for(i=ans.size()-1;ans[i]=='0'&&i>=1;i--); ans=ans.substr(0,i+1);
if(carry!=0) ans.push_back(char('0'+carry));
reverse(ans.begin(),ans.end());
return ans;
}
void getans()
{
ans[1]="2";
string two("2");
for(int i=2;i<=200;i++)
{
ans[i]=add(multiply(ans[i-1],two),two);
}
}
int main()
{
getans();
int n;cin>>n;
cout<<ans[n]<<endl;
return 0;
}
问题 D: 【递归与递推】2的幂次方
题目:
任何一个正整数都可以用2的幂次方表示。例如:
137=27+23+20
同时约定方次用括号来表示,即ab 可表示为a(b)。
由此可知,137可表示为:
2(7)+2(3)+2(0)
进一步:7=22+2+20 (21用2表示)
3=2+20
所以最后137可表示为:
2(2(2)+2+2(0))+2(2+2(0))+2(0)
又如:
1315=210 +28 +25 +2+1
所以1315最后可表示为:
2(2(2+2(0))+2)+2(2(2+2(0)))+2(2(2)+2(0))+2+2(0)
输入
一行,一个正整数(n≤20000)
输出
符合约定的n的0,2表示(在表示中不能有空格)
思路:
参考算法:递归输出某十进制数的二进制表示的算法。(主要是在递归回溯的时候才输出,而非计算出来就输出。)
#include <stdio.h>
void fun(int n)
{
int t;
if(n==0)
return;
else
{
t=n%2;
fun(n/2);
printf("%d",t);
}
}
int main()
{
fun(5);
return 0;
}
本着从易到难的原则,可以考虑先实现将137表示为:2(7)+2(3)+2(0)的程序。
关于加号的输出:可以考虑判断当前项是否二进制序列的最高位(x递归到下一层slove(x/2,bit+1)的数x/2是0,即返回了)。是最高位则当前项左侧不输出“+”,否则在当前项左侧输出“+”.
关于把指数也转变为0和2的序列:在输出每一项时判断指数是否超过1,超过则先输出“2(”,然后把该指数与0传入递归函数,递归显示该指数的表示。然后在输出后半边括号。
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
void slove(int x,int bit)
{
if(x==0) return ;
else
{
slove(x>>1,bit+1);
if(x%2)///回溯的时候才输出
{
if(bit==1)
{
if(x!=1) cout<<"+2";
else cout<<2