信息学奥赛一本通(C++版)第二部分 基础算法 第三章 递推算法

版权声明: https://blog.csdn.net/Lu_Anlai/article/details/80588349

第三章 递推算法



本章内容小节

递推算法

  递推法是一种重要的数学方法,在数学的各个领域中都有广泛的运用,也是计算机用于数值计算的一个重要算法。这种算法特点是:一个问题的求解需一系列的计算,在已知条件和所求问题之间总存在着某种相互联系的关系,在计算时,如果可以找到前后过程之间的数量关系(即递推式),那么,从问题出发逐步推到已知条件,此种方法叫逆推。无论顺推还是逆推,其关键是要找到递推式。这种处理问题的方法能使复杂运算化为若干步重复的简单运算,充分发挥出计算机擅长于重复处理的特点。

  递推算法的首要问题是得到相邻的数据项间的关系(即递推关系)。递推算法避开了求通项公式的麻烦,把一个复杂的问题的求解,分解成了连续的若干步简单运算。一般说来,可以将递推算法看成是一种特殊的迭代算法。



题解

T1312 : 昆虫繁殖

【题目描述】
  科学家在热带森林中发现了一种特殊的昆虫,这种昆虫的繁殖能力很强。每对成虫过x个月产y对卵,每对卵要过两个月长成成虫。假设每个成虫不死,第一个月只有一对成虫,且卵长成成虫后的第一个月不产卵(过X个月产卵),问过Z个月以后,共有成虫多少对?0≤X≤20,1≤Y≤20,X≤Z≤50。

【输入】
  x,y,z的数值。

【输出】
  过Z个月以后,共有成虫对数。

【输入样例】

1 2 8

【输出样例】

37

【答案&代码】

#include<stdio.h>
long long adult[64],born[64];
int main(void){
    int x,y,z;
    scanf("%d%d%d",&x,&y,&z);
    for(int i=1;i<=x;i++)
        adult[i]=1,born[i]=0;
    for(int i=x+1;i<=z+1;i++)
        born[i]=y*adult[i-x],
        adult[i]=adult[i-1]+born[i-2];
    printf("%lld",adult[z+1]); 
    return 0;
}

T1313 : 位数问题

【题目描述】
  在所有的N位数中,有多少个数中有偶数个数字3?由于结果可能很大,你只需要输出这个答案对12345取余的值。

【输入】
  读入一个数N。

【输出】
  输出有多少个数中有偶数个数字3。

【输入样例】

2

【输出样例】

73

【答案&代码】

#include<stdio.h>
int f[1001][2];
int main(void){
    int n;
    f[1][0]=9,f[1][1]=1;
    scanf("%d",&n);
    for(int i=2,x;i<=n;i++){
        x=f[1][0];
        if(i==n)
            x--;
        f[i][0]=(f[i-1][0]*x+f[i-1][1])%12345,
        f[i][1]=(f[i-1][1]*x+f[i-1][0])%12345;
    }
    printf("%d",f[n][0]);
    return 0;   
}

T1314: 过河卒

【题目描述】
  棋盘上A点有一个过河卒,需要走到目标B点。卒行走的规则:可以向下、或者向右。同时在棋盘上的某一点有一个对方的马(如C点),该马所在的点和所有跳跃一步可达的点称为对方马的控制点,如图3-1中的C点和P1,……,P8,卒不能通过对方马的控制点。棋盘用坐标表示,A点(0,0)、B点(n, m) (n,m为不超过20的整数),同样马的位置坐标是需要给出的,C≠A且C≠B。现在要求你计算出卒从A点能够到达B点的路径的条数。
这里写图片描述

【输入】
  给出n、m和C点的坐标。

【输出】
  从A点能够到达B点的路径的条数。

【输入样例】

8 6 0 4

【输出样例】

1617

【答案&代码】

#include<stdio.h>
unsigned long long dp[21][21]={0};
int main(void){
    int n,m;
    scanf("%d%d",&n,&m);
    int mx,my;
    scanf("%d%d",&mx,&my);
    dp[0][0]=1;
    for(int i=0;i<=n;i++){
        for(int j=0;j<=m;j++)
            if(i==mx&&j==my||
                i==mx-1&&j==my-2||
                i==mx-2&&j==my-1||
                i==mx-2&&j==my+1||
                i==mx+1&&j==my-2||
                i==mx+2&&j==my-1||
                i==mx+1&&j==my+2
            )
                dp[i][j]=0;
            else if(i==0&&j!=0)
                dp[i][j]=dp[i][j-1];
            else if(j==0&&i!=0)
                dp[i][j]=dp[i-1][j];
            else if(i==0&&j==0)
                dp[i][j]=1;
            else
                dp[i][j]=dp[i-1][j]+dp[i][j-1];
    }
    printf("%lld",dp[n][m]);
    return 0;
}

T1188: 菲波那契数列(2)

【题目描述】
  菲波那契数列是指这样的数列: 数列的第一个和第二个数都为1,接下来每个数都等于前面2个数之和。

  给出一个正整数a,要求菲波那契数列中第a个数对1000取模的结果是多少。

【输入】
  第1行是测试数据的组数n,后面跟着n行输入。每组测试数据占1行,包括一个正整数a(1 ≤ a ≤ 1000000)。

【输出】
  n行,每行输出对应一个输入。输出应是一个正整数,为菲波那契数列中第a个数对1000取模得到的结果。

【输入样例】

4
5
2
19
1

【输出样例】

5
1
181
1

【答案&代码】

#include<stdio.h>
int fun(int n){
    int F1=1,F2=1,Fn=1;
    for(int i=3;i<=n;i++)
        Fn=F1+F2,F1=F2,F2=Fn,Fn%=1000,F1%=1000,F2%=1000;
    return Fn;
}
int main(void){
    int n;
    scanf("%d",&n);
    for(int i=0;i<n;i++){
        int temp;
        scanf("%d",&temp);
        printf("%d\n",fun(temp)%1000); 
    }
    return 0;
}

T1189: Pell数列

【题目描述】
  Pell数列a1,a2,a3,…的定义是这样的,a1=1,a2=2,…,an=2an-1+an-2(n>2)。

  给出一个正整数k,要求Pell数列的第k项模上32767是多少。
【输入】
  第1行是测试数据的组数n,后面跟着n行输入。每组测试数据占1行,包括一个正整数k (1≤k<1000000)。

【输出】
  n行,每行输出对应一个输入。输出应是一个非负整数。

【输入样例】

2
1
8

【输出样例】

1
408

【答案&代码】

#include<stdio.h>
unsigned int a[1000005]={0,1,2}; 
int main(void){
    for(int i=3;i<=1000000;i++)
        a[i]=(a[i-1]<<1)+a[i-2],a[i]%=32767;
    int n;
    scanf("%d",&n);
    for(int i=0,temp;i<n;i++){
        scanf("%d",&temp);
        printf("%d\n",a[temp]); 
    }
    return 0;
} 

T1190: 上台阶

【题目描述】
  楼梯有n(71>n>0)阶台阶,上楼时可以一步上1阶,也可以一步上2阶,也可以一步上3阶,编程计算共有多少种不同的走法。

【输入】
  输入的每一行包括一组测试数据,即为台阶数n。最后一行为0,表示测试结束。

【输出】
  每一行输出对应一行输入的结果,即为走法的数目。

【输入样例】

1
2
3
4
0

【输出样例】

1
2
4
7

【答案&代码】

#include<stdio.h>      
long long d[110];
int main(void){
    d[1]=1,d[2]=2,d[3]=4;
    for(int i=4;i<=100;i++)
        d[i]=d[i-1]+d[i-2]+d[i-3];
    int n;
    while(scanf("%d",&n)==1&&n)
        printf("%lld\n",d[n]);
    return 0;
}

T1191: 流感传染

【题目描述】
  有一批易感人群住在网格状的宿舍区内,宿舍区为n*n的矩阵,每个格点为一个房间,房间里可能住人,也可能空着。在第一天,有些房间里的人得了流感,以后每天,得流感的人会使其邻居传染上流感,(已经得病的不变),空房间不会传染。请输出第m天得流感的人数。

【输入】
  第一行一个数字n,n不超过100,表示有n*n的宿舍房间。

  接下来的n行,每行n个字符,’.’表示第一天该房间住着健康的人,’#’表示该房间空着,’@’表示第一天该房间住着得流感的人。

  接下来的一行是一个整数m,m不超过100。

【输出】
  输出第m天,得流感的人数。

【输入样例】

5
....#
.#.@.
.#@..
#....
.....
4

【输出样例】

16

【答案&代码】

#include<stdio.h>
char map[101][102],temp[101][102];
#include<string.h>
int main(void){
    int n,m;
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        scanf("%s",map[i]+1);
    scanf("%d",&m);
    for(int l=1;l<m;l++){
        memcpy(temp,map,sizeof(map));
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)
                if(temp[i][j]=='.'&&
                    (temp[i-1][j]=='@'
                    ||temp[i+1][j]=='@'
                    ||temp[i][j-1]=='@'
                    ||temp[i][j+1]=='@'
                    )
                )
                map[i][j]='@';
    }
    int sum=0;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
            if(map[i][j]=='@')
                sum+=1;
    printf("%d",sum);
    return 0;
}

T1192: 放苹果

【题目描述】
  把M个同样的苹果放在N个同样的盘子里,允许有的盘子空着不放,问共有多少种不同的分法?(用K表示)5,1,1和1,5,1 是同一种分法。

【输入】
  第一行是测试数据的数目t(0≤t≤20)。以下每行均包含二个整数M和N,以空格分开。1≤M,N≤10。

【输出】
  对输入的每组数据M和N,用一行输出相应的K。

【输入样例】

1
7 3

【输出样例】

8

【答案&代码】

#include<stdio.h>
int f[101][101];
int main(void){
    int n,m,t;
    for(int i=0;i<=100;i++)
        for(int j=0;j<=100;j++)
            if(i==0||j==1)
                f[i][j]=1;
            else
                if(j>i)
                    f[i][j]=f[i][i];
                else
                    f[i][j]=f[i][j-1]+f[i-j][j];
    scanf("%d",&t);
    for(int i=0;i<t;i++)
        scanf("%d%d",&m,&n),
        printf("%d\n",f[m][n]);
    return 0;
}

T1193: 吃糖果

【题目描述】
  名名的妈妈从外地出差回来,带了一盒好吃又精美的巧克力给名名(盒内共有 N 块巧克力,0 < N < 20)。妈妈告诉名名每天可以吃一块或者两块巧克力。假设名名每天都吃巧克力,问名名共有多少种不同的吃完巧克力的方案。例如:如果N=1,则名名第1天就吃掉它,共有1种方案;如果N=2,则名名可以第1天吃1块,第2天吃1块,也可以第1天吃2块,共有2种方案;如果N=3,则名名第1天可以吃1块,剩2块,也可以第1天吃2块剩1块,所以名名共有2+1=3种方案;如果N=4,则名名可以第1天吃1块,剩3块,也可以第1天吃2块,剩2块,共有3+2=5种方案。现在给定N,请你写程序求出名名吃巧克力的方案数目。

【输入】
  输入只有1行,即整数N。

【输出】
  输出只有1行,即名名吃巧克力的方案数。

【输入样例】

4

【输出样例】

5

【答案&代码】

#include<stdio.h>
int f[32]={0,1,2,3,5};
int fun(int n){
    if(f[n]!=0)
        return f[n];
    else if(n==0)
        return 0;
    else
        return f[n]=fun(n-1)+fun(n-2);
}
int main(void){
    int n;
    scanf("%d",&n);
    printf("%d",fun(n));
    return 0;
}

T1194: 移动路线

【题目描述】
  X桌子上有一个m行n列的方格矩阵,将每个方格用坐标表示,行坐标从下到上依次递增,列坐标从左至右依次递增,左下角方格的坐标为(1,1),则右上角方格的坐标为(m,n)。

  小明是个调皮的孩子,一天他捉来一只蚂蚁,不小心把蚂蚁的右脚弄伤了,于是蚂蚁只能向上或向右移动。小明把这只蚂蚁放在左下角的方格中,蚂蚁从

  左下角的方格中移动到右上角的方格中,每步移动一个方格。蚂蚁始终在方格矩阵内移动,请计算出不同的移动路线的数目。

  对于1行1列的方格矩阵,蚂蚁原地移动,移动路线数为1;对于1行2列(或2行1列)的方格矩阵,蚂蚁只需一次向右(或向上)移动,移动路线数也为1……对于一个2行3列的方格矩阵,如下图所示:

(2,1) (2,2) (2,3)
(1,1) (1,2) (1,3)

  蚂蚁共有3种移动路线:

  路线1:(1,1) → (1,2) → (1,3) → (2,3)

  路线2:(1,1) → (1,2) → (2,2) → (2,3)

  路线3:(1,1) → (2,1) → (2,2) → (2,3)

【输入】
  输入只有一行,包括两个整数m和n(0 < m+n ≤ 20),代表方格矩阵的行数和列数,m、n之间用空格隔开。

【输出】
  输出只有一行,为不同的移动路线的数目。

【输入样例】

2 3

【输出样例】

3

【答案&代码】

#include<stdio.h>
long long map[32][32];
int main(void){
    for(int i=1;i<=21;i++)
        map[i][1]=map[1][i]=1;
    for(int i=2;i<=31;i++)
        for(int j=2;j<=21;j++)
            map[i][j]=map[i-1][j]+map[i][j-1];
    int m,n;
    scanf("%d%d",&m,&n);
    printf("%lld",map[m][n]);
    return 0;
}

T1195: 移动路线

【题目描述】
  一个给定的正整数序列,在每个数之前都插入+号或-号后计算它们的和。比如序列:1、2、4共有8种可能的序列:
(+1) + (+2) + (+4) = 7

(+1) + (+2) + (-4) = -1

(+1) + (-2) + (+4) = 3

(+1) + (-2) + (-4) = -5

(-1) + (+2) + (+4) = 5

(-1) + (+2) + (-4) = -3

(-1) + (-2) + (+4) = 1

(-1) + (-2) + (-4) = -7

  所有结果中至少有一个可被整数k整除,我们则称此正整数序列可被k整除。例如上述序列可以被3、5、7整除,而不能被2、4、6、8……整除。注意:0、-3、-6、-9……都可以认为是3的倍数。

【输入】
  输入的第一行包含两个数:N(2< N < 10000)和k(2 < k < 100),其中N代表一共有N个数,k代表被除数。第二行给出序列中的N个整数,这些整数的取值范围都0到10000之间(可能重复)。

【输出】
  如果此正整数序列可被k整除,则输出YES,否则输出NO。(注意:都是大写字母)

【输入样例】

3 2
1 2 4

【输出样例】

NO

【答案&代码】

#include<stdio.h>
bool f[10005][205];
int a[10005];
int main(void){
    int n,k;
    scanf("%d%d",&n,&k);
    for(int i=1;i<=n;i++)
        scanf("%d",a+i);
    f[1][(a[1]%k+k)%k]=1;
    for(int i=2;i<=n;i++)
        for(int j=0;j<k;j++)
            f[i][j]=(f[i-1][((j-a[i])%k+k)%k]||f[i-1][(j+a[i])%k]);
    if(f[n][0])
        printf("YES");
    else
        printf("NO");
    return 0;
}

T1196: 踩方格

【题目描述】
  有一个方格矩阵,矩阵边界在无穷远处。我们做如下假设:

  1. 每走一步时,只能从当前方格移动一格,走到某个相邻的方格上;

  2. 走过的格子立即塌陷无法再走第二次;

  3. 只能向北、东、西三个方向走;

  请问:如果允许在方格矩阵上走n步,共有多少种不同的方案。2种走法只要有一步不一样,即被认为是不同的方案。

【输入】
  允许在方格上行走的步数n(n≤20)。

【输出】
  计算出的方案数量。

【输入样例】

2

【输出样例】

7

【答案&代码】

#include<stdio.h>
int a[21]={0,3,7};
int main(void){
    int n;
    scanf("%d",&n);
    for(int i=3;i<=n;i++)
        a[i]=2*a[i-1]+a[i-2];
    printf("%d",a[n]);
    return 0;
}

T1197: 山区建小学

【题目描述】
  政府在某山区修建了一条道路,恰好穿越总共m个村庄的每个村庄一次,没有回路或交叉,任意两个村庄只能通过这条路来往。已知任意两个相邻的村庄之间的距离为di(为正整数),其中,0 < i < m。为了提高山区的文化素质,政府又决定从m个村中选择n个村建小学(设0 < n ≤ m < 500)。请根据给定的m、n以及所有相邻村庄的距离,选择在哪些村庄建小学,才使得所有村到最近小学的距离总和最小,计算最小值。

【输入】
  第1行为m和n,其间用空格间隔

  第2行为m−1 个整数,依次表示从一端到另一端的相邻村庄的距离,整数之间以空格间隔。

  例如:

10 3
2 4 6 5 2 4 3 1 3

  表示在10个村庄建3所学校。第1个村庄与第2个村庄距离为2,第2个村庄与第3个村庄距离为4,第3个村庄与第4个村庄距离为6,…,第9个村庄到第10个村庄的距离为3。

【输出】
  各村庄到最近学校的距离之和的最小值。

【输入样例】

10 2
3 1 3 1 1 1 1 1 3

【输出样例】

18

【答案&代码】

#include<stdio.h>
int f[510][510],dis[510][510],d[510],s[510][510];
int min(int a,int b){
    return a<b?a:b;
}
int abs(int n){
    return n>0?n:(-n);
}
int dist(int i,int j){
    int x=0;
    int mid=(i+j)/2;
    for(int k=i;k<=j;k++)
        x+=dis[k][mid];
    return x;
}
int main(void){
    int m,n;
    scanf("%d%d",&m,&n);
    for(int i=2,temp;i<=m;i++)
        scanf("%d",&temp),
        d[i]=d[i-1]+temp;
    for(int i=1;i<=m;i++)
        for(int j=1;j<=m;j++)
            if(i==j)
                dis[i][j]=0;
            else
                dis[i][j]=dis[j][i]=abs(d[j]-d[i]);
    for(int i=1;i<=m;i++)
        for(int j=1;j<=m;j++)
            s[i][j]=dist(i,j);
    for(int i=1;i<=m;i++)
        for(int j=1;j<=m;j++)
            f[i][j]=0X3F3F3F3F;
    for(int i=1;i<=m;i++)
        f[i][i]=0,f[i][1]=s[1][i];
    for(int i=2;i<=m;i++)
        for(int j=2;j<=min(i,n);j++)
            for(int k=j-1;k<=i-1;k++)
                if(i!=j)
                    f[i][j]=min(f[i][j],f[k][j-1]+s[k+1][i]);
    printf("%d",f[m][n]); 
    return 0;
}

猜你喜欢

转载自blog.csdn.net/Lu_Anlai/article/details/80588349