递推小结

这篇博客总结了几个递推关系在实际问题中的应用,包括超级楼梯、骨牌铺法、数塔问题、过河卒问题和位数问题等。通过递推公式和动态规划思想解决这些问题,例如斐波那契数列、动态规划状态转移方程等。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

———————————–递推———————————-
超级楼梯

Description
有一楼梯共M级,刚开始时你在第一级,若每次只能跨上一级或二级,要走上第M级,共有多少种走法?

Input
输入数据首先包含一个整数N,表示测试实例的个数,然后是N行数据,每行包含一个整数M(1<=M<=40),表示楼梯的级数。

Output
对于每个测试实例,请输出不同走法的数量。

Sample Input
2
2
3

Sample Output
1
2

思路:
由于一次只能上两步或者一步,所以推广到一般情况到第i级可以从第i-1级跨一步上来,也可以是从第i-2级跨两步上来,所以一般式为f[i]=f[i-1]+f[i-2];即为斐波拉契数列


骨牌铺法

Description
有1×n的一个长方形,用一个1×1、1×2和1×3的骨牌铺满方格。例如当n=3时为1×3的方格。此时用1×1、1×2和1×3的骨牌铺满方格,共有四种铺法。

Input
包含多组测试数据。每组测试数据包含一个正整数。

Output
每组测试数据输出一个整数,占一行。

Sample Input
3

Sample Output
4
思路:
推广到一般的极限情况,当我们铺到最后一块的时候,我们可能是铺三种中的任何一块,则有三种情况:
1,铺的最后一块是1*1的,有f[i-1]种方法;
2,铺的最后一块是1*2的,有f[i-2]种方法;
3,铺的最后一块是1*3的,有f[i-3]种方法;
综上有f[i-1]+f[i-2]+f[i-3]种方法;

#include<cstdio>
#include <iostream>
#include <cstring>
using namespace std;

int main()
{
   int n;
   long long a[109];
   a[1]=1;
   a[2]=2;
   a[3]=4;
   for(int i=4;i<=100;i++)
    a[i]=a[i-1]+a[i-2]+a[i-3];
   while(cin>>n)
   {
      cout<<a[n]<<endl;
   }
    return 0;
}

数塔问题

Description
设有一个三角形树塔,第一层1个整数,第二层2个整数,第三层3个整数,…。从第一层出发可以向下走或右下走。若要求从第一层开始,到最下一层,请找出一条路径,使路径之和最大,只要输出路径的和。

例如下图所示的树塔,从第一次出发,路径整合最大的一条路径为13,8,26,15,13,路径的和为86。

    13

    11      8

    12      7    26

      6    14    15      8

    12      7    13    24    11

Input
包含多组测试数据。每组测试数据的第一行为一个整数n(n<10),表示数塔的层数,从第2行至n+1行,每行有若干个数据,表示数塔中的数值。

Output
每组测试数据输出占一行,输出路径和最大的路径值。

Sample Input
5
13
11 8
12 7 26
6 14 15 8
12 7 13 24 11

Sample Output
86

思路:
运用到动态规划的思想。我们可以从最下面的一层开始往前倒推,可以得出一般的情况的递推式:s[i-1][j]=a[i-1][j]+max(s[i][j],s[i][j+1]);

#include<cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn=1000;
int a[maxn+1][maxn+1],dp[maxn+1][maxn+1];
int main()
{
    int n;
    while(cin>>n)
    {
        for(int i=1; i<=n; i++)
        {
            for(int j=1; j<=i; j++)
                cin>>a[i][j];
        }
        int ans=a[n][1];
        memset(dp,0,sizeof dp);
        for(int i=n; i>0; i--)
            dp[n][i]=a[n][i];
        for(int i=n-1; i>=1; i--)
            for(int j=1; j<=i; j++)
                dp[i][j]=max(dp[i+1][j],dp[i+1][j+1])+a[i][j];
        cout<<dp[1][1]<<endl;
    }
    return 0;
}

过河卒
Description
中国象棋中的“马”按照“日”字规则移动。假定马所在位置用二维坐标(x,y)来表示,则它下一步可以移动的8个位置分别为(x-1,y-2),(x-2,y-1),(x-2,y+1),(x-1,y+2),(x+1,y-2),(x+1,y+2),(x+2,y-1),(x+2,y+1)。

棋盘上A点有一个过河卒,需要移动到目标点B,过河卒移动的规则:可以向下(横坐标+1)或向右(纵坐标+1)。在棋盘上的点C处有一个对方的马,该马所在的点和所有移动一步可以到达的点称为对方马的控制点。过河卒不能通过对方马的控制点。

设定棋盘上各点的横纵坐标都大于等于0。A点坐标为(0,0),B点坐标(n,m),其中n和m均不超过20,点C≠A且C≠B。

现在输入B点坐标和C点的坐标,要你计算出过河卒从A点能够到达B点的路径的条数。

Input
包含多组测试数据,每组测试数据占一行,包含4个整数(大于等于0小于等于20),分别为点B和点C的横纵坐标。

Output
每组测试数据输出占一行,每行输出一个整数,表示从A点能够到达B点的路径的条数。

Sample Input
4 8 2 4

Sample Output
0

思路:

dp[i][j]记录走到此格的方法数,如果此格不能走,那么直接为零;
由于只能向右走或者向下走,所以可得一般推广式:dp[i][j]=dp[i-1][j]+dp[i][j-1];要注意处理边界情况如果是第一行中的格子那么就只能通过向右行走达到即dp[i][j]=dp[i][j-1],如果是第一列的格子那么就只能通过向下行走得到即dp[i][j]=dp[i-1][j];
还要注意的是本题不用longlong会爆掉;

#include<cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
long long dp[23][23];
int bx,by,x,y;

int main()
{
    while(cin>>bx>>by>>x>>y)
    {
        for(int i=0; i<=20; i++)
            for(int j=0; j<=20; j++)
                dp[i][j]=1;
        dp[x][y] = 0;
        dp[x+2][y+1] = 0;
        dp[x+1][y+2] = 0;
        if(x>=1)dp[x-1][y+2] = 0;
        if(x>=2)dp[x-2][y+1] = 0;
        if(x>=2&&y>=1)dp[x-2][y-1] = 0;
        if(x>=1&&y>=2)dp[x-1][y-2] = 0;
        if(y>=2)dp[x+1][y-2] = 0;
        if(y>=1)dp[x+2][y-1] = 0;
        for(int i=0; i<=bx; i++)
        {
            for(int j=0; j<=by; j++)
            {
                if(!dp[i][j]) continue;
                else if(i==0&&j==0) continue;
                else if(i==0) dp[i][j]=dp[i][j-1];
                else if(j==0) dp[i][j]=dp[i-1][j];
                else dp[i][j]=dp[i-1][j]+dp[i][j-1];
            }

        }
        cout<<dp[bx][by]<<endl;
    }
    return 0;
}

昆虫繁殖

Description
科学家在热带森林中发现了一种特殊的昆虫,这种昆虫的繁殖能力很强。

每对成虫过x个月产y对卵,每对卵要过两个月长成成虫。

假设每个成虫不死,第一个月只有一对成虫,且卵长成成虫后的第一个月不产卵(过X个月产卵),问过Z个月以后,共有成虫多少对?

0=<x<=201<=y<=20,x=<z<=50

Input
包含多组测试数据,每组测试数据包含3个整数(x,y,z)。

Output
每组测试数据输出占一行,每行一个整数,即Z个月以后的成虫对数。

Sample Input 
1 2 8

Sample Output
37



唉!刚开始的时候还在想怎么找到一个公式把这道题的情况全部概括出来,结果死活没搞出来。。。
我们可以用两个数组分别储存成虫和幼虫的个数,那么可以得到一般的递推式:
ch[i]=ch[i-1]+jin[i-2]//这是由于两个月幼虫会长成成虫
jin[i]=y*ch[i-x]//此式代表第i个月出生的幼虫为x月前的成虫个数乘以y对得到。


#include<cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;

int main()
{
   int x,y,z;
   long long ch[1009],jin[1009];
   while(cin>>x>>y>>z)
   {
       for(int i=0;i<x;i++)
        ch[i]=1,jin[i]=0;
       for(int i=x;i<=z;i++)
       {
           if(i>2) ch[i]=ch[i-1]+jin[i-2];
           else ch[i]=ch[i-1];
           jin[i]=y*ch[i-x];
       }
       cout<<ch[z]<<endl;
   }
    return 0;
}


位数问题

Description
在所有的N位数中,有多少个数中有偶数个数字3?
由于结果可能很大,你只需要输出这个答案对12345取余的值。
Input
包含多组测试数据,每组测试数据包含一个整数n。

Output
每组测试数据输出占一行,包含一个整数,即有多少个数中有偶数个数字3。

Sample Input
2

Sample Output
73

思路:
a[i]储存的是i位数中三的个数为奇数的情况
b[i]储存的是i位数中三的个数为偶数的情况
那么有奇偶之间的加法关系,奇数加一变为偶数,偶数加一变为奇数,可得递推式:
a[i]=a[i-1]*8+b[i-1];
b[i]=b[i-1]*8+a[i-1];
由于我们考虑的都是在此数左边加数的情况所以一共有1-9共九个数可以添加,我们添加的数为3那么就会改变奇偶性,其他的八种情况都不会;

#include<cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
int a[1009],b[1009];

int main()
{
    a[1]=1;//ji数
    b[1]=8;//ou数
    for(int i=2;i<=1000;i++)
    {
        a[i]=(a[i-1]*9+b[i-1])%12345;
        b[i]=(b[i-1]*9+a[i-1])%12345;
    }
    b[1]=9;
    int n;
    while(cin>>n)
    {
        cout<<b[n]<<endl;
    }
    return 0;
}

邮票问题
Description
设有已知面额的邮票m种,每种有n张,用总数不超过n张的邮票,能从面额1开始,最多连续组成多少面额。
(1≤m≤100,1≤n≤100,1≤邮票面额≤255)
Input
包含多组测试数据,每组测试数据包含2行。

第一行包括2个整数m和n的值。

第二行有m个整数,表示各种名额的值。

数据之间用一个空格隔开。

Output
每组测试数据包含1个整数,即连续面额数的最大值。

Sample Input
3 4
1 2 4

Sample Output
14

思路:
s[i]储存能够成面额i所需的最少张数;
如果满足条件得到的张数比原有计算值小,那么就更新值;
一般递推式为:s[i]=s[i-a[j]]+1;

#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
int main()
{
    int n,m,a[10009];
    while(cin>>n>>m)
    {
        memset(a,0,sizeof a);
        for (int i=0; i<n; i++)
            cin>>a[i];
        sort(a,a+n);
        if (a[0]!=1)
        {
            cout<<"0"<<endl;
            continue;
        }
        int s[30000]= {0};
        s[1]=1;
        for (int i=2; ; i++)
        {
            for (int j=0; j<n && a[j]<=i ; j++)
            {
                if ( s[i]==0 || s[i]>s[i-a[j]]+1)
                    s[i]=s[i-a[j]]+1;
            }
            if (s[i]>m)
            {
                cout<<i-1<<endl;
                break;
            }
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值