基础--递推

递推:

递推学不好,动规做不了

基本思路:

确定状态转移方程:从第 i-1 位怎么转移到第 i 位 (找规律)

例题:

例一:骨牌 

有 2*n 的一个长方形方格,用一个1*2 的骨牌铺满方格。求输入一个 n ,最多有多少种铺法(n = 3 ,有如下图三种铺法)

 

基本思路:用递推方法找规律

  •  当 n=1 时,只有一种铺法
  •  当 n=2 时,有两种铺法(可以并列竖排,可以并列横排)
  •  当 n=3 时,有三种铺法(如上图)

推出规律:对一般的 n 来说,第一个骨牌有两种放置情况

  1. 横着放,占两格
  2. 竖着放,占一格

若第一个骨牌是横着放,则需要对剩下的 n-2 进行排列,此时方法为f(n-2)个

若第一个骨牌是竖着放,则需要对剩下的 n-1 进行排列,此时方法为f(n-1)个

所以,递推式为 f(n)=f(n-1)+f(n-2)

1 f[1]=1,f[2]=2;
2 for(int i=3;i<=n;i++)
3 f[i]=f[i-1]+f[i-2];
4 cout<<f[n];
5 return 0;

 

例二:昆虫繁殖 

有一种昆虫,每过x个月产y对卵,每对卵需要2个月才能长成成虫。假设每对成虫不死,并且每对卵刚张成成虫的第一个月不产卵(过x个月产卵)。第一个月只有一对成虫,问z个月后有多少对成虫?

输入一行三个数,分别为x,y,z。x≤20,1≤y≤20,z≤50。

Example Input

        1 2 8

Example Output

        37

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 int a[1000],b[1000];  //a[i]为老虫子的个数,b[i]为小虫子的个数 
 4 int main()
 5 {
 6     int x,y,z;
 7     cin>>x>>y>>z;
 8     for(int i=1;i<=x;i++) 
 9      a[i]=1;  //在前x个月里只有一对老虫子(还没生出小虫子)
10     for(int i=x+1;i<=z+1;i++)
11     {
12         b[i]=y*a[i-x]; //这个月生出了多少对小虫子  
13         a[i]=a[i-1]+b[i-2]; //现在这个月的老虫子数量=上个月的老虫子数量+上上个月的小虫子数量(已经长大了) 
14     } 
15     cout<<a[z+1];  //求的是现在一共有多少对老虫子,如果求总共有多少对虫子应该是a[z+1]+b[z+1]; 
16     return 0;
17 }

 

思路:用数组 a[ i ] 代表第 i 个月的老虫子数量,用数组 b[ i ] 表示第 i 个月的小虫子的数量 模拟递推求解

(如果每隔k个月老虫子会死怎么算) a[ i ] - = a[ i-k ] ?

例三:位数问题

问题描述:在所有的n位数中,有多少个数中有偶数个数字3?由于结果很大,你只需要输出对这个答案对12345取余的值
输入格式:输入一个数字n
输出格式:输出有多少个数中有偶数个数字3
输入样例:2
输出样例:73
数据范围:1<=n<=1000

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 int f[1000][2]; //f[i][0]表示前i位数有偶数个3的有几种情况,f[i][1]表示有奇数个三的有几种情况   
 4 int main()
 5 {
 6     int n;
 7     cin>>n;
 8     f[1][0]=9;  // 0 1 2 4 5 6 7 8 9  有0个3
 9     f[1][1]=1;  // 3
10     for(int i=2;i<=n;i++)
11     {
12         int x=f[i-1][0];
13         if(i==n) x--;   //因为第一位数可能是0,所以先让第一位可以是0,最后一位再不取0(这样最后一位就少了0这种情况,所以X--),相当于倒过来;
14         f[i][0]=f[i-1][0]*x+f[i-1][1];
15         f[i][1]=f[i-1][1]*x+f[i-1][0];
16     }
17     cout<<f[n][0];
18     return 0;
19 } 

 

思路:递推 从第 n-1 位推到第 n 位

 

原来 n-1 位有偶数个3,再加上的一位有9种可能(0,1,2,4,5,6,7...)原来 n-1 位有奇数个3,再加上的一位只有1种可能(3),变成偶数个3

 

所以递推式为:f [ i ] [ 0 ] = f [ i - 1 ] [ 0 ] *9 + f [ i - 1 ] [ 1 ]

例四:过河卒问题

棋盘上 AA 点有一个过河卒,需要走到目标 BB 点。卒行走的规则:可以向下、或者向右。同时在棋盘上 CC 点有一个对方的马,该马所在的点和所有跳跃一步可达的点称为对方马的控制点。因此称之为“马拦过河卒”。

棋盘用坐标表示,AA 点 (0, 0)(0,0)、BB 点 (n, m)(n,m),同样马的位置坐标是需要给出的。

现在要求你计算出卒从 AA 点能够到达 BB 点的路径的条数,假设马的位置是固定不动的,并不是卒走一步马走一步。

输入格式

一行四个正整数,分别表示 BB 点坐标和马的坐标。

输出格式

一个整数,表示所有的路径条数。

输入输出样例

输入 #1
6 6 3 3
输出 #1
6
 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cmath>
 4 using namespace std;
 5 long long f[25][25];
 6 bool b[25][25];
 7 int main() 
 8 {
 9     int x,y,xm,ym,i,j;
10     cin>>x>>y>>xm>>ym;
11     x+=2;
12     y+=2;
13     xm+=2;
14     ym+=2;
15     f[2][2]=1;
16     for(int i=1;i<=x;i++)
17      for(int j=1;j<=y;j++)
18       b[i][j]=true;
19     b[xm][ym]=false;
20     b[xm-1][ym-2]=false;       
21     b[xm-2][ym-1]=false;
22     b[xm-2][ym+1]=false;
23     b[xm-1][ym+2]=false;
24     b[xm+1][ym+2]=false;
25     b[xm+2][ym+1]=false;
26     b[xm+1][ym-2]=false;
27     b[xm+2][ym-1]=false;
28     for(int i=2; i<=x; i++) //i是纵坐标y;j是横坐标x;
29       for(int j=2; j<=y; j++) 
30         {
31             if(b[i][j]==false) continue;
32             f[i][j]=max(f[i-1][j]+f[i][j-1],f[i][j]);
33         }
34     cout<<f[x][y];
35     return 0;
36 }

基本思路:f [ i ] [ j ] = f [ i - 1 ] [ j ] + f [ i ] [ j - 1 ]  也就是说 到达某一个点的路径数 等于 到达它上面的路径数 加 到达它左面的路径数

边界:f [1][1]=1

但注意,状态转移方程不能这么写, 必须用max比较,否则 f [1][1]会被覆盖成0;

例五:汉诺塔问题(递推 / 递归)

例六:放苹果(递推 / 搜索)

例七:判断整除

例八:踩方格

补充:当递推遇见高精度

数楼梯

题目描述

楼梯有 NN 阶,上楼可以一步上一阶,也可以一步上二阶。

编一个程序,计算共有多少种不同的走法。

输入格式

一个数字,楼梯数。

输出格式

输出走的方式总数。

输入输出样例

输入 #1
4
输出 #1
5

说明/提示

  • 对于 60\%60% 的数据,N \leq 50N50;
  • 对于 100\%100% 的数据,N \leq 5000N5000。
 1 #include<iostream>
 2 using namespace std;
 3 int n,f[5010][5010],len;
 4 void jiafa(int k)
 5 {
 6     for(int i=1; i<=len; i++)
 7         f[k][i]=f[k-1][i]+f[k-2][i];
 8     for(int i=1; i<=len; i++)
 9         if(f[k][i]>=10)
10         {
11             f[k][i+1]+=f[k][i]/10;
12             f[k][i]%=10;
13             if(f[k][len+1]>0)len++;
14         }
15 }
16 int main()
17 {
18     cin>>n;
19     len=1;
20     f[1][1]=1;//预处理
21     f[2][1]=2;//预处理
22     for(int i=3; i<=n; i++)//开始计算
23         jiafa(i);
24     for(int i=len; i>=1; i--)//输出
25         cout<<f[n][i];
26     return 0;
27 }

思路:f [ i ] [ j ] 表示斐波那契数列第 i 项的第 j 位 (如 f [4][1]表示 f [4]的第1位数即为5)

(4.6日再写)

五种典型的递推关系:

1.斐波那契数列:

2.Hanoi塔问题:

3.平面分割问题:

4.卡特兰数:

5.第二类 Stirling 数:

 

posted @ 2021-04-05 15:36  -Sky-  阅读(232)  评论(0编辑  收藏  举报