基础递归与递推总结

基础递归与递推总结

前记:还是很基础的专题啊,不过后面有刚开始学会很头疼的汉诺塔问题。emmm,算入门了吧。

文章目录

    • 基础递归与递推总结
    • 【递归与递推】猴子摘桃
    • 【递归与递推】移梵塔
    • 【递归与递推】九连环
    • 【递归与递推】2的幂次方
    • 【递归与递推】攀天梯
    • 【递归与递推】递归函数(reduce)
    • 【递归与递推】滑雪
    • 【回溯法】马拦过河卒
    • 【递归与递推】乐乐的棋盘
    • 【递归与递推】数的计数
    • 【递归与递推】骑士游历问题
    • 【递归与递推】分式
    • 【递归与递推】兔子繁殖
    • 【递归与递推 】帕斯卡的旅行
    • 【递推与递归】棋子移动
    • 【递归与递推】Hanoi双塔问题

【递归与递推】猴子摘桃

题目描述:
果园里种了很多桃树,当桃树开始结果的时候,猴子便会成群结队地前来摘桃。猴子们第一天会摘掉桃子的一半还多一个,第二天再摘第一天剩下的一半还多一个,以后每天均摘掉上一天剩下的一半还多一个,到第n天时,树上就只剩下两个桃子了。请问果园里原来共多少个桃子?
输入:
输入一个正整数n(n<50),表示天数。
输出:
输出果园里原来共有的桃子数。
样例输入:
4
样例输出:
30
暴力记录,直接解即可。

#include 
using namespace std;
int main()
{
    int n,m=2;
    scanf("%d",&n);
    n--;
    while(n--)
    {
        m=m+1;
        m=m*2;
    }
    printf("%d\n",m);
    return 0;
}

【递归与递推】移梵塔

题目描述:
有三根柱A、B、C,在A柱上有n块盘片,所有盘片都是大片在下面,小片放在大片上面。并依次编好序号。现要将A上的n块盘片移到C柱上,每次只能移动一片,而且在同一根柱子上必须保持上面的盘片比下面的盘片小,请输出移动方法。
输入:
仅一个整数n(n≤20),表示A柱上的盘片数。
输出:
输出盘片的移动步骤。
样例输入:
3
样例输出:
A-1-C
A-2-B
C-1-B
A-3-C
B-1-A
B-2-C
A-1-C
汉诺塔问题,其实也就手动模拟一下就清楚了(模拟一个盘子和两个盘子就够了,太多容易晕),只剩一个肯定是A到C,否则需要先把它挪到B,然后从B再挪到C。

#include 
using namespace std;
void fun(char a,char b,char c,int n)
{
    if(n==1) printf("%c-%d-%c\n",a,n,c);
    else
    {
        fun(a,c,b,n-1);
        printf("%c-%d-%c\n",a,n,c);
        fun(b,a,c,n-1);
    }
}
int main()
{
    int n;
    char a,b,c;
    a='A',b='B',c='C';
    scanf("%d",&n);
    fun(a,b,c,n);
    return 0;
}

【递归与递推】九连环

题目描述:
九连环是由九个彼此套接的圆环和一根横杆组成,九个环从左到右依次为l~9,每个环有两种状 态:1和0,1表示环在杆上,0表示环不在杆上。初始状态是九个环都在杆上,即:111111111,目标状态是九个环都不在杆上,即:000000000,由初始状态到目标状态的变化规则是:
(1)第一环为无论何时均可自由上下横行;
(2)第二只环只有在第一环为1时,才能自由上下;
(3)想要改变第n(n>2)个环的状态,需要先使第一到第(n-2)环均为下杆,且第n-1个环为上杆,而与第n+l个到第九环状态无关;
(4)每改变一个环,记为一步。
现在九连环由111111111变到000000000,求中间第i步的状态。
输入:
仅包含一个整数i。
输出:
仅包含中间第i步的状态。如果输入的步数大于实际变换所需的步数,则输出-1。
样例输入:
2
500
样例输出:
010111111
-1
从第九个环开始,一个一个往前模拟,按理说能过,但是我的代码经常谜之错误。。。AC了也不知道咋过的。

#include
using namespace std;
int huan[10]= { 1,1,1,1,1,
                1,1,1,1,1
              };
int res[400][10] = {0};
int n = 9;
int step = 0;
int sum = 0;
void cut(int a[])
{
    for(int i = 1; i <= n; i++)
        res[sum][i] = a[i];
    sum ++;
}
void up(int, int);
void down(int n, int s)
{
    if(n == 1)
    {
        huan[1] = 0;
        cut(huan);
        return;
    }
    if(n == 2)
    {
        huan[2] = 0;
        cut(huan);
        huan[1] =0;
        cut(huan);
        return;
    }
    down(n-2,step);
    huan[n] = 0;
    cut(huan);
    up(n-2,step);
    down(n-1,step);
}
void up(int n, int s)
{
    if(n == 1)
    {
        huan[1] = 1;
        cut(huan);
        return;
    }
    if(n == 2)
    {
        huan[1] = 1;
        cut(huan);
        huan[2] =1;
        cut(huan);
        return;
    }
    up(n-1,step);
    down(n-2,step);
    huan[n] = 1;
    cut(huan);
    up(n-2,step);
}
int main()
{
    while(cin>>step)
    {
        cut(huan);
        down(n,step);
        if(step > sum - 1)
            cout<<-1<<endl;
        else
        {
            for(int i = 1; i <=n; i++)
                cout<<res[step][i];
            cout<<endl;
        }
        sum = 0;
        memset(res,0,sizeof(res));
        for(int i = 0; i < 10; i++)
            huan[i]= 1;
    }
    return 0;
}

【递归与递推】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表示(在表示中不能有空格)
样例输入:
137
样例输出:
2(2(2)+2+2(0))+2(2+2(0))+2(0)
数据只有20000,就是说最多2的15次方,可以列举出转换二进制后15种数字的写法,也可以递归直接写,我这里用的是第一种,需要注意的是2(0)直接写作2。

#include
using namespace std;
string er(int n)
{
    string str1="";
    while(n)
    {
        if(n%2==0)
            str1+='0';
        else
            str1+='1';
        n/=2;
    }
    return str1;
}
string str1[15]= {"0","2(0)","2","2+2(0)","2(2)","2(2)+2(0)","2(2)+2","2(2)+2+2(0)","2(2+2(0))","2(2+2(0))+2(0)","2(2+2(0))+2","2(2+2(0))+2+2(0)","2(2+2(0))+2(2)","2(2+2(0))+2(2)+2(0)","2(2+2(0))+2(2)+2"};
int main()
{
    int n;
    scanf("%d",&n);
    string str=er(n);
    int len=str.size();
    int flag=0;
    for(int i=1; i<=len; i++)
    {
        int u=len-i;
        if(str[u]=='1')
        {
            if(flag==0)
            {
                if(i==len-1&&u==1)
                    cout<<"2";
                else
                    cout<<"2("<<str1[u]<<")";
                flag=1;
            }
            else
            {
                if(i==len-1&&u==1)
                    cout<<"+2";
                else
                    cout<<"+2("<<str1[u]<<")";
            }
        }
    }
    cout<<endl;
    return 0;
}

【递归与递推】攀天梯

题目描述:
北武当山主峰四周几乎都是陡壁悬崖,只有一条人造“天梯”.可攀,天梯由n级就山凿筑的石阶组成,欢欢打算通过天梯攀上北武当山主峰。攀天梯时,他有时一步一级石阶,有时一步两级,那么,他攀上这n级的天梯有多少种不同的方法?
输入:
一个整数n(1≤n≤80)。
输出:
一个整数,表示欢欢攀上这n级天梯的方法数。
样例输入:
5
样例输出:
8
手动模拟了前6个,1,2,3,5,8,13,是不是很眼熟?嗯,又是斐波那契数列。交一次 ,竟然WA了,发现是数据爆long long了,祭出我的高精度加法,AC。

#include
using namespace std;
string F[1001];
string add(string x,string y)
{
    int len1=x.size();
    int len2=y.size();
    int lenx=max(len1,len2);
    int lenn=min(len1,len2);
    reverse(x.begin(),x.end());
    reverse(y.begin(),y.end());
    string str;
    int v=0;
    for(int i=0; i<lenn; i++)
    {
        int u=(x[i]-'0')+(y[i]-'0')+v;
        if(u>=10)
        {
            v=1;
            u-=10;
        }
        else
            v=0;
        char ch=(char)(u+'0');
        str+=ch;
    }
    if(v==1)
    {
        for(int i=lenn; i<lenx; i++)
        {
            int u;
            if(len1>len2)
                u=(x[i]-'0')+v;
            else
                u=(y[i]-'0')+v;
            if(u>=10)
            {
                v=1;
                u-=10;
            }
            else
                v=0;
            str+=(char)(u+'0');
        }
    }
    else
    {
        for(int i=lenn; i<lenx; i++)
        {
            int u;
            if(len1>len2)
                u=x[i]-'0';
            else
                u=y[i]-'0';
            str+=(char)(u+'0');
        }
    }
    if(v==1)
        str+='1';
    reverse(str.begin(),str.end());
    return str;
}
void feibo()
{
    string a1="1";
    string a2="1";
    F[0]="1";
    F[1]="1";
    for(int i=2; i<=1000; i++)
    {
        F[i]=add(F[i-1],F[i-2]);
    }
}
int main()
{
    feibo();
    int n;
    scanf("%d",&n);
    cout<<F[n];
    printf("\n");
    return 0;
}

【递归与递推】递归函数(reduce)

题目描述:
考虑如下的3参数递归函数w(a,b,c);
如果a≤0或b≤0或c≤0,则w(a,b,c)=1;
如果a>20或b>20或c>20,则w(a,b,c)=w(20,20,20);
如果a其他情况下:w(a,b,c)=w(a-1,b,c)+w(a-1,b-1,c)+w(a-1,b,c-1)-w(a-1, b-1,c-1)。
设计一个程序,快速计算w(a,b,c)并给出结果。
输入:
1行整数,包含3个数值,分别对应a、b和c的值。
输出:
一个数,即w(a,b,c)的结果。
样例输入:
50 50 50
样例输出:
1048576
开个三维数组记录,然后。。。就完了

#pragma GCC optimize("Ofast","inline","-ffast-math")
#pragma GCC target("avx,sse2,sse3,sse4,mmx")
#pragma GCC optimize(2)
#include
using namespace std;
int ans=0;
int f[100][100][100];
int w(int a,int b,int c)
{
    if(a<=0||b<=0||c<=0)
        return 1;
    if(f[a][b][c]>0)
        return f[a][b][c];
    else if(a>20||b>20||c>20)
    {
        return f[a][b][c]=w(20,20,20);
    }
    else if(a<b&&b<c)
        return f[a][b][c]=w(a,b,c-1)+w(a,b-1,c-1)-w(a,b-1,c);
    else
        return f[a][b][c]=w(a-1,b,c)+w(a-1,b-1,c)+w(a-1,b,c-1)-w(a-1,b-1,c-1);
}
int main()
{
    int a,b,c;
    scanf("%d%d%d",&a,&b,&c);
    ans=w(a,b,c);
    printf("%d\n",ans);
    return 0;
}

【递归与递推】滑雪

题目描述:
Michael喜欢滑雪。这并不奇怪,因为滑雪的确很刺激。可是为了获得速度,滑的区域必须向下倾斜,而且当你滑到坡底,你不得不再次走上坡或者等待升降机来载你。Michael想知道在一个区域中最长的滑坡。区域由一个二维数组给出。数组的每个数字代表点的高度。
下面是一个例子:
1 2 3 4 5
16 17 18 19 6
15 24 25 20 7
14 23 22 21 8
13 12 11 10 9
一个人可以从某个点滑向上下左右相邻四个点之一,当且仅当高度减小。在上面的例子中,一条可行的滑坡为24-17-16-1(从24开始,在1结束)。当然25-24-23-…-3-2-1更长。事实上,这是最长的一条。
输入:
第1行为表示区域的二维数组的行数R和列数C(1≤R,C≤l00)。下面是R行,每行有C个数,代表高度。
输出:
输出区域中最长滑坡的长度。
样例输入:
5 5
1 2 3 4 5
16 17 18 19 6
15 24 25 20 7
14 23 22 21 8
13 12 11 10 9
样例输出:
25
提示:
一个人可以从某个点滑向上下左右相邻四个点之一,当且仅当高度减小。在上面的例子中,一条可行的滑坡为24-17-16-1(从24开始,在l结束)。当然25-24-23-…-3-2-1更长。事实上,这是最长的一条。
直接搜索,不管是dfs还是bfs,都会超时,需要用到记忆化搜索(其实就是开个数组记录)。

#include
using namespace std;
typedef long long ll;
int r,c;
int maxn=0;
int next1[4][2]={{1,0},{-1,0},{0,1},{0,-1}};
int a[105][105];
int flag[105][105];
int dfs(int x,int y)
{
    if(flag[x][y])
        return flag[x][y];
    else
    {
        int p=1;
        for(int i=0;i<4;i++)
        {
            int xx=x+next1[i][0];
            int yy=y+next1[i][1];
            if(!(xx<0||xx>r||yy<0||yy>c||a[xx][yy]<=a[x][y]))
            {
                p=max(p,dfs(xx,yy)+1);//这样就不用回溯了
            }
        }
        flag[x][y]=p;
        return p;
    }
}
int main()
{
    cin>>r>>c;
    for(int i=0;i<r;i++)
    {
        for(int j=0;j<c;j++)
        {
            scanf("%d",&a[i][j]);
        }
    }
    for(int i=0;i<r;i++)
    {
        for(int j=0;j<c;j++)
        {
            maxn=max(maxn,dfs(i,j));
        }
    }
    cout<<maxn<<endl;
    return 0;
}

【回溯法】马拦过河卒

题目描述:
棋盘上A点有一个过河卒,需要走到目标B点。卒行走的规则:可以向下、或者向右。同时在棋盘上C点有一个对方的马,该马所在的点和所有跳跃一步可达的点称为对方马的控制点。因此称之为“马拦过河卒”。
棋盘用坐标表示,A点(0, 0)、B点(n, m)(n, m为不超过20的整数),同样马的位置坐标是需要给出的。现在要求你计算出卒从A点能够到达B点的路径的条数,假设马的位置是固定不动的,并不是卒走一步马走一步。
基础递归与递推总结_第1张图片
输入:
一行四个数据,分别表示B点坐标和马的坐标。(保证所有的数据有解)
输出:
一个数据,表示所有的路径条数。
样例输入:
6 6 3 3
样例输出:
6
暴力搜索会TLE,用int会WA,正解是记忆化搜索+long long。

#include
using namespace std;
typedef long long ll;
int n,m,r,c;
int next1[8][2]={{2,1},{2,-1},{-2,1},{-2,-1},{1,2},{-1,2},{1,-2},{-1,-2}};
int next2[2][2]={{1,0},{0,1}};
int flag[105][105];
bool judge(int x,int y)
{
    if(x<0||x>n||y<0||y>m)
        return false;
    return true;
}
long long road[105][105];
long long dfs(int x,int y)
{
    if(road[x][y])
    {
        return road[x][y];
    }
    if(!judge(x,y))
        return 0;
    if(x==n&&y==m)
        return 1;
    for(int i=0;i<2;i++)
    {
        int xx=x+next2[i][0];
        int yy=y+next2[i][1];
        if(flag[xx][yy]==0&&judge(xx,yy))
        {
            flag[xx][yy]=1;
            road[x][y]+=dfs(xx,yy);
            flag[xx][yy]=0;
        }
    }
    return road[x][y];
}
int main()
{
    cin>>n>>m>>r>>c;
    flag[r][c]=1;
    for(int i=0;i<8;i++)
    {
        int xx=r+next1[i][0];
        int yy=c+next1[i][1];
        flag[xx][yy]=1;
    }
    long long road1=dfs(0,0);
    printf("%lld\n",road1);
    return 0;
}

【递归与递推】乐乐的棋盘

题目描述:
乐乐有一个棋盘,共有m行n列,一只棋子从左上角开始,向右下角移动,每次只能向下或向右移动一次。然而这个棋盘中有一些障碍物,这些障碍物使得这个棋子不能进入这些格子,问这个棋子从左上角到达右下角共有多少种不同的移法?
如果到达不了,则输出0。
输入:
第1行:两个整数m,n (0 后面有m行,每行有n个数(0或1),如果是1,则表示这个方格中有障碍物。
输出:
求得的方案数。
跟上个题差不多,就是要用高精度。

#include
using namespace std;
typedef long long ll;
string add(string x,string y)
{
    int len1=x.size();
    int len2=y.size();
    int lenx=max(len1,len2);
    int lenn=min(len1,len2);
    reverse(x.begin(),x.end());
    reverse(y.begin(),y.end());
    string str;
    int v=0;
    for(int i=0; i<lenn; i++)
    {
        int u=(x[i]-'0')+(y[i]-'0')+v;
        if(u>=10)
        {
            v=1;
            u-=10;
        }
        else
            v=0;
        char ch=(char)(u+'0');
        str+=ch;
    }
    if(v==1)
    {
        for(int i=lenn; i<lenx; i++)
        {
            int u;
            if(len1>len2)
                u=(x[i]-'0')+v;
            else
                u=(y[i]-'0')+v;
            if(u>=10)
            {
                v=1;
                u-=10;
            }
            else
                v=0;
            str+=(char)(u+'0');
        }
    }
    else
    {
        for(int i=lenn; i<lenx; i++)
        {
            int u;
            if(len1>len2)
                u=x[i]-'0';
            else
                u=y[i]-'0';
            str+=(char)(u+'0');
        }
    }
    if(v==1)
        str+='1';
    reverse(str.begin(),str.end());
    return str;
}
int n,m;
int next2[2][2]={{1,0},{0,1}};
int flag[105][105];
bool judge(int x,int y)
{
    if(x<=0||x>n||y<=0||y>m)
        return false;
    return true;
}
string road[105][105];
string dfs(int x,int y)
{
    if(road[x][y]!="0")
    {
        return road[x][y];
    }
    if(!judge(x,y))
        return 0;
    if(x==n&&y==m)
        return "1";
    for(int i=0;i<2;i++)
    {
        int xx=x+next2[i][0];
        int yy=y+next2[i][1];
        if(flag[xx][yy]==0&&judge(xx,yy))
        {
            flag[xx][yy]=1;
            road[x][y]=add(road[x][y],dfs(xx,yy));
            flag[xx][yy]=0;
        }
    }
    return road[x][y];
}
int main()
{
    while(~scanf("%d%d",&n,&m))
    {
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=m;j++)
            {
                scanf("%d",&flag[i][j]);
                road[i][j]="0";
            }
        }
        string road1=dfs(1,1);
        cout<<road1<<endl;
    }
    return 0;
}

【递归与递推】数的计数

题目描述:
我们要求找出具有下列性质数的个数(包含输入的自然数n),先输入一个自然数n(n≤1000),然后对此自然数按照如下方法进行处理:
(1)不作任何处理;
(2)在它的左边加上一个自然数,但该自然数不能超过原数的一半;
(3)加上数后,继续按此规则进行处理,直到不能再加自然数为止。
输入:
一个正整数n。
输出:
符合以上性质的数的个数。
对原数字进行除二处理,没除一次就p+1,直到为0,最后输出p+1。

#include
#define maxn 500010
using namespace std;
typedef long long ll;
int p=0;
void fun(int m)
{
    if(m!=0)
    {
        for(int i=1;i<=m;i++)
        {
            p++;
            fun(i/2);
        }
    }
}
int main()
{
    int n;
    cin>>n;
    n/=2;
    fun(n);
    cout<<p+1<<endl;
    return 0;
}

【递归与递推】骑士游历问题

题目描述:
设有一个m*n的棋盘(2≤m≤50,2≤n≤50),在棋盘上任一点有一个中国象棋“马”,马走的规则为:马走日字;马只能向右走。当m,n给出后,同时给出马起始的位置和终点的位置,试找出从起点到终点所有路径的数目。
输入:
m,n,xl,yl,x2,y2(分别表示棋盘大小、起点坐标和终点坐标)。
输出:
路径数目(若不存在,则输出0)。
样例输入:
30 30 1 15 5 15
样例输出:
8
递归的话按上面的记忆化搜索就行,但是会被卡样例(不知道为什么,数据太大?),最后用dp解决。

#include
#include
using namespace std;
long long f[101][101];
int n,m,x1,x2;
int main()
{
    int y1,y2;
    scanf("%d%d",&n,&m);
    scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
    f[x1][y1]=1;
    for(int i=x1;i<=n;i++)
     for(int j=1;j<=m;j++)
       {
            if(i-2>0&&j-1>0)  f[i][j]+=f[i-2][j-1];
            if(i-2>0&&j+1<=m) f[i][j]+=f[i-2][j+1];
            if(i-1>0&&j-2>0)  f[i][j]+=f[i-1][j-2];
            if(i-1>0&&j+2<=m) f[i][j]+=f[i-1][j+2];
       }
    cout<<f[x2][y2]<<endl;
    return 0;
}

【递归与递推】分式

题目描述:
有n(1<=n<=40)条分数线的繁分式如下,请把它化简为一般分式。
基础递归与递推总结_第2张图片
输入:
仅包含n。
输出:
第一行为分子,第二行为分母:
样例输入:
1
样例输出:
3
2
用gcd解决最大公因数问题,然后分子分母同除之,即解决。

#include 
using namespace std;
int ans=0;
int zi=2,mu=3;
void fun(int x)
{
    while(x--)
    {
        int t=zi;
        zi=mu;
        mu=mu+t;
    }
}
int gcd(int x,int y)
{
    if(y==0)
        return x;
    else
        return gcd(y,x%y);
}
void yuefen(int mu,int zi)
{
    int u=gcd(mu,zi);
    zi/=u;
    mu/=u;
}
int main()
{
    int n;
    cin>>n;
    n--;
    fun(n);
    yuefen(mu,zi);
    cout<<mu<<endl;
    cout<<zi<<endl;
    return 0;
}

【递归与递推】兔子繁殖

题目描述:
兔子有很强的繁殖能力。1对成年的兔子每个月可以生育一对幼年的兔子,而1对幼年的兔子经过m个月之后,就会长成1对成年的兔子。当一开始有1对成年兔子时,经过d个月以后,共有多少对兔子?你的任务是计算出一对成年兔子经过d个月后,共有多少对兔子,假设整个过程中没有兔子死亡。
输入:
输入有多组数据,每组占l行,为两个整数m,d(1≤m≤10,l≤d≤I00),当m=d=0时表示结束。
输出:
每行为每组数据对应最后得到的兔子数。
样例输入:
2 3
3 5
0 0
样例输出:
5
9
递推式是这样的:f[i]=f[i-1]+f[i-m](前面m个是一个一个递增的)需要注意的是 数据量很大(提供一组样例输入:1 100,输出应为:1267650600228229401496703205376),需要用到高精度加法。

#include
using namespace std;
string f[100005]={"0"};
string add(string x,string y)
{
    int len1=x.size();
    int len2=y.size();
    int lenx=max(len1,len2);
    int lenn=min(len1,len2);
    reverse(x.begin(),x.end());
    reverse(y.begin(),y.end());
    string str;
    int v=0;
    for(int i=0; i<lenn; i++)
    {
        int u=(x[i]-'0')+(y[i]-'0')+v;
        if(u>=10)
        {
            v=1;
            u-=10;
        }
        else
            v=0;
        char ch=(char)(u+'0');
        str+=ch;
    }
    if(v==1)
    {
        for(int i=lenn; i<lenx; i++)
        {
            int u;
            if(len1>len2)
                u=(x[i]-'0')+v;
            else
                u=(y[i]-'0')+v;
            if(u>=10)
            {
                v=1;
                u-=10;
            }
            else
                v=0;
            str+=(char)(u+'0');
        }
    }
    else
    {
        for(int i=lenn; i<lenx; i++)
        {
            int u;
            if(len1>len2)
                u=x[i]-'0';
            else
                u=y[i]-'0';
            str+=(char)(u+'0');
        }
    }
    if(v==1)
        str+='1';
    reverse(str.begin(),str.end());
    return str;
}
int main()
{
    int m,d;
    while(scanf("%d%d",&m,&d))
    {
        if(!m && !d)
            break;
        f[0]="1";
        for(int i=1; i<=m; i++)
        {
            stringstream str;
            str<<i;
            string str1;
            str>>str1;
            f[i]=add(str1,"1");
        }
        for(int i=m+1; i<=d; i++)
            f[i]=add(f[i-1],f[i-m]);
        cout<<f[d]<<endl;
    }
    return 0;
}

【递归与递推 】帕斯卡的旅行

题目描述在一个n×n个方格的游戏板中,每个方格中有一个非负整数。游戏的目标是从游戏板的左上角沿任何合法路径移动到右下角。任何一个方格内的数字规定了离开本方格的一步必须移动的方格数。如果移动的一步越出了游戏板,则这个方向的移动是禁止的。每一步移动只能是向下或向右的。考虑如下图所示的4×4的板,这里正体字表示出发位置,斜体字表示目的位置。后面显示了从出发位置到目的位置的三条路径,其中隐去了与每条路径无关的数字。
基础递归与递推总结_第3张图片
输入:
输入含有n+l行,第1行是游戏板的行数n(4≤n≤34),接下来是n个数据行,每行含有n个0~9的数字,中间没有空格。
输出:
在1行中输出从左上角到右下角的路径数(注:输出的路径数使用长整型数据类型)。
样例输入:
4
8888
8888
8888
8888
样例输出:
0
还是记忆化搜索,但是要注意,递归之前要判断是否越界,否则会出现数组越界导致的段错误(提交的里面唯一的运行错误,痛心)。

#include
typedef long long ll;
using namespace std;
ll n;
bool judge(ll x,ll y)
{
    if(x>n||y>n)
        return false;
    else
        return true;
}
ll a[105][105];
ll aa[105][105];
ll dfs(int x,int y)
{
    if(!judge(x,y))
        return 0;
    if(aa[x][y]!=0)
        return aa[x][y];
    if(x==n&&y==n)
        return 1;
    else
    {
        int xx,yy;
        xx=x+a[x][y];
        yy=y+a[x][y];
        if(judge(xx,y)) aa[x][y]=aa[x][y]+dfs(xx,y);
        if(judge(x,yy)) aa[x][y]=aa[x][y]+dfs(x,yy);
    }
    return aa[x][y];
}
int main()
{
    scanf("%lld",&n);
    for(int i=1;i<=n;i++)
    {
        string str;
        cin>>str;
        for(int j=1;j<=n;j++)
        {
            a[i][j]=str[j-1]-'0';
        }
    }
    ll ans=dfs(1,1);
    printf("%lld\n",ans);
    return 0;
}

【递推与递归】棋子移动

题目描述
有2n(n≥4)个棋子排成一行,开始位置为白子全部在左边,黑子全部在右边。例如n=5时:00000*****。移动棋子的规则是:每次必须同时移动相邻两个棋子,颜色不限,可以左移也可以右移到空位上去,但不能调换两个棋子的左右位置。每次移动必须跳过若干个棋子(不能平移),要求最后能移成黑白相间的一行棋子。如n=5时,成为:
在这里插入图片描述

输入:
仅输入黑棋和白棋的数目n(4≤n≤1000)。
输出:
输出各行每一步的移动结果,最后一行输出总移动步数,其中用“O”(大写字母)表示白棋,用“*”表示黑棋。“__”(两个下划线)表示两个空位。
样例输入:
5
样例输出:
基础递归与递推总结_第4张图片
n==4时,可直接暴力输出,如果比4大,移动两步则可使其-1。至于移动速度,直接(n-4)*2+5即可。

#include 
#define MAXN 2002
using namespace std;
int n;
char a[MAXN];
inline void move(int now, int to)
{
    swap(a[now], a[to]);
    swap(a[now + 1], a[to + 1]);
    for(register int i = 1; i <= n * 2 + 2; i++) cout << a[i];
    cout << endl;
    return;
}
void func(int x)
{
    if(x == 4)
    {
        move(4, 9);
        move(8, 4);
        move(2, 8);
        move(7, 2);
        move(1, 7);
        return;
    }
    move(x, x * 2 + 1);
    move(x * 2 - 1, x),
    func(x - 1);
    return;
}
int main()
{
    cin >> n;
    for(register int i = 1; i <= n; i++)
    {
        a[i] = 'O';
        a[i + n] = '*';
    }
    a[n * 2 + 1] = a[n * 2 + 2] = '_';
    func(n);
    cout << "step=" << 5 + (n - 4) * 2;
    return 0;
}

【递归与递推】Hanoi双塔问题

题目描述:
给定A,B,C三根足够长的细柱,在A柱上放有2n个中间有空的圆盘,共有n个不同的尺寸,每个尺寸都有两个相同的圆盘,注意这两个圆盘是不加区分的(下图为n=3的情形)。现要将这些圆盘移到C柱上,在移动过程中可放在B柱上暂存。要求:
基础递归与递推总结_第5张图片
(1)每次只能移动一个圆盘;
(2) A、B、C三根细柱上的圆盘都要保持上小下大的顺序;
任务:设An为2n个圆盘完成上述任务所需的最少移动次数,对于输入的n,输出An。
输入:
输入一个正整数n,表示在A柱上放有2n个圆盘。
输出:
输出一个正整数,为完成上述任务所需的最少移动次数An。
样例输入:
2
样例输出:
6
提示:
对于100% 数据, 1<=n<=200
递推式:f[i]=f[i-1]*2+2,用高精度解决(哇,这个专题终于结束了,费脑子啊。。。不过也熟练掌握了之前不会的记忆化搜索,也是有所收获的吧)。

#include 
using namespace std;
string add(string x,string y)
{
    int len1=x.size();
    int len2=y.size();
    int lenx=max(len1,len2);
    int lenn=min(len1,len2);
    reverse(x.begin(),x.end());
    reverse(y.begin(),y.end());
    string str;
    int v=0;
    for(int i=0; i<lenn; i++)
    {
        int u=(x[i]-'0')+(y[i]-'0')+v;
        if(u>=10)
        {
            v=1;
            u-=10;
        }
        else
            v=0;
        char ch=(char)(u+'0');
        str+=ch;
    }
    if(v==1)
    {
        for(int i=lenn; i<lenx; i++)
        {
            int u;
            if(len1>len2)
                u=(x[i]-'0')+v;
            else
                u=(y[i]-'0')+v;
            if(u>=10)
            {
                v=1;
                u-=10;
            }
            else
                v=0;
            str+=(char)(u+'0');
        }
    }
    else
    {
        for(int i=lenn; i<lenx; i++)
        {
            int u;
            if(len1>len2)
                u=x[i]-'0';
            else
                u=y[i]-'0';
            str+=(char)(u+'0');
        }
    }
    if(v==1)
        str+='1';
    reverse(str.begin(),str.end());
    return str;
}
string f[205]={"0"};
int main()
{
    int n;
    scanf("%d",&n);
    f[0]="0";
    f[1]="2";
    for(int i=2;i<=n;i++)
    {
        f[i]=add(f[i-1],f[i-1]);
        f[i]=add(f[i],"2");
    }
    cout<<f[n]<<endl;
    return 0;
}

你可能感兴趣的:(递推与递归,训练营)