递归小结

双色Hanoi塔问题
Description
设A、B、C是3 个塔座。开始时,在塔座A 上有一叠共n 个圆盘,这些圆盘自下而上,由大到小地叠在一起。各圆盘从小到大编号为1,2,……,n,奇数号圆盘着蓝色,偶数号圆盘着红色。现要求将塔座A 上的这一叠圆盘移到塔座B 上,并仍按同样顺序叠置。在移动圆盘时应遵守以下移动规则:
规则(1):每次只能移动1 个圆盘;
规则(2):任何时刻都不允许将较大的圆盘压在较小的圆盘之上;
规则(3):任何时刻都不允许将同色圆盘叠在一起;
规则(4):在满足移动规则(1)-(3)的前提下,可将圆盘移至A,B,C 中任一塔座上。
试设计一个算法,用最少的移动次数将塔座A 上的n个圆盘移到塔座B 上,并仍按同样顺序叠置。
对于给定的正整数n,编程计算最优移动方案。
Input
包含多组测试数据,每组测试数据包含一个正整数n。
Output
每组测试数据包含多行。每一行描述一步一定方案。

每一行由一个正整数k和2个字符c1和c2组成,表示将第k个圆盘从塔座c1移到塔座c2上。

各数据之间用一个空格隔开。
Sample Input
3

Sample Output
1 A B
2 A C
1 B C
3 A B
1 C A
2 C B
1 A B

思路:
推广到一般情况,想要将第i个盘子放到B柱上,那么需要将其上的i-1个盘子放到过度柱上,然后将第i个盘子放到目标柱上去,最后将原柱当做过渡柱,将那i-1个盘子,从过渡柱搬到目标柱上,所以用到了递归,而双色和单色并没有区别

#include
#include 
#include 
#include 
using namespace std;
void mov(int n,char a,char b)
{
    cout<" "<" "<void digui(int n,char a,char c,char b)
{
    if(n==1) mov(n,a,b);
    else
    {
        digui(n-1,a,b,c);
        mov(n,a,b);
        digui(n-1,c,a,b);
    }
}
int main()
{
    int n;
    while(cin>>n)
    {
        digui(n,'A','C','B');
    }
    return 0;
}

2的幂次方

Description
任何一个正整数都可以用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)
Input
包含多组测试数据,每组测试数据包含一个正整数n(n≤20000)

Output
符合约定的n的0,2表示(在表示中不能有空格)
Sample Input
137
1315

Sample Output
2(2(2)+2+2(0))+2(2+2(0))+2(0)
2(2(2+2(0))+2)+2(2(2+2(0)))+2(2(2)+2(0))+2+2(0)

思路:
每次递归先找到最接近目标值N的最大的小于等于的数值,然后记录他是二的多少次方,如果二者相等那么不需要再添加加号,如果不是那么还需添加加号,但是有一点如果此位数为一,那么直接输出“2+“而不需要括号了,如果不是这样他还会递归到下一层,那么会出现多余的2(0);
此外分成两层递归,一边递归指数cnt,另一边递归剩下的值(n-tmp);

#include 
using namespace std;
void digui(int n)
{
    if(n==1)
    {
        cout<<"2(0)";
        return;
    }
    if(n==2)
    {
        cout<<"2";
        return;
    }
    int tmp=1,cnt=0;
    while(tmp<=n)
    {
        tmp=tmp<<1;
        cnt++;
    }
    tmp=tmp>>1;
    cnt--;
    if(n==tmp)
    {
        cout<<"2(";
        digui(cnt);
        cout<<")";
    }
    else
    {
        if(cnt==1)
        {
            cout<<"2+";
            digui(n-tmp);//此处也可为cnt,因为最终剩下的结果一定是1
        }
        else
        {
            cout<<"2(";
            digui(cnt);
            cout<<")+";
            digui(n-tmp);
        }
    }
}
int main()
{
    int n;
    while(cin>>n)
    {
        digui(n);
        cout<return 0;
}

数的计数
Description
我们要求找出具有下列性质数的个数(包含输入的自然数n):
先输入一个自然数n(n≤1000), 然后对此自然数按照如下方法进行处理:
1.不作任何处理;
2.在它的左边加上一个自然数,但该自然数不能超过原数(输入的n)的一半;
3.加上数后,继续按此规则进行处理,直到不能再加自然数为止。
例如:n=6
则满足条件的数为6 ,16,26,126,36,136共6个
Input
包含多组测试数据,每组测试数据包含一个正整数n

Output
输出满足条件的数的个数。

Sample Input
6

Sample Output
6

思路:
dp[j][i]表示在i的左边添加j的情况数目,可得递推式
for(int k=0; k<=j/2; k++)
dp[j][i]+=dp[k][j];

#include 
#include 
using namespace std;
long long ans=0;
int dp[509][1009];
int main()
{
    int n;
    while(cin>>n)
    {
        memset(dp,0,sizeof dp);
        ans=0;
        for(int i=1; i<=n; i++)
            dp[0][i]=1;
        for(int i=0; i<=n; i++)
            for(int j=1; j<=i/2; j++)
                for(int k=0; k<=j/2; k++)
                    dp[j][i]+=dp[k][j];
         for(int i=0;i<=n/2;i++)
            ans+=dp[i][n];
         cout<return 0;
}

集合划分问题
Description
设S是一个具有n个元素的集合,S={a1,a2,……,an},现将S划分成k个满足下列条件的子集合S1,S2,……,Sk ,且满足:
(1) 所有子集均不等于空
(2) 任意两个子集都不相交。
(3) 所有子集的并集等于集合s
则称S1,S2,……,Sk为集合S的一个划分。
请你设计程序计算包含n个元素的集合s划分成k个子集的划分数S(n,k)是多少?
例如,当n=4 时,集合S={1,2,3,4}可以划分为15 个不同的非空子集如下:
{ {1},{2},{3},{4} }, { {1,2},{3},{4} }, { {1,3},{2},{4} },
{ {1,4},{2},{3} } , { {2,3},{1},{4} }, { {2,4},{1},{3} },
{ {3,4},{1},{2} }, { {1,2},{3,4} }, { {1,3},{2,4} },
{ {1,4},{2,3} }, { {1,2,3},{4} }, { {1,2,4},{3} },
{ {1,3,4},{2} }, { {2,3,4},{1} }, { {1,2,3,4} }。
显然,有S(4,1)=1; S(4,2)=7; S(4,3)=6; S(4,4)=1。
Input
包含多组测试数据,每组测试数据包含2个正整数n和k。

Output
输出S(n,k)。

Sample Input
4 1
4 2
4 3
4 4

Sample Output
1
7
6
1

思路:
将一个集合划分为指定个数的子集,我们需要考虑对于一个单独的数,我们是将其放入已有的子集中,还是将其放入一个新的子集呢?
这样就可以得到递归式 digui(n-1,m-1)+digui(n-1,m)*m
前者是将其放入一个新的集合中,后者是将其放入已有的集合中的情况,而已有的集合个数为m,所以要乘以m;

#include 
#include 
using namespace std;
long long ans=0;
int n,k;
long long digui(int n,int m)
{
   if(m==1||n==m) return 1;
   return digui(n-1,m-1)+digui(n-1,m)*m;
}
int main()
{

    while(cin>>n>>k)
    {
      cout<return 0;
}

装错信封

Description
某人写了n封信和n个信封,如果所有的信都装错了信封。求所有的信都装错信封共有多少种不同情况。

Input
包含多组测试数据,每组测试数据包含一个正整数n(即信或信封的数量,不超过15)。

Output
输出装错情况的种类数。

Sample Input
1
2
3
4

Sample Output
0
1
2
9
思路:
错排公式

#include 
#include 
using namespace std;
long long ans=0;
int n,k;

int digui(int n)
{
   if(n==1) return 0;
   if(n==2) return 1;
   return (n-1)*(digui(n-1)+digui(n-2));
}
int main()
{

    while(cin>>n)
    {
      cout<return 0;
}

你可能感兴趣的:(蓝桥杯典型试题)