程序设计语言综合设计(第 5 章)

  • 第 5 章
    • 5_cow
    • 5_bee
    • 5_stairs
    • 5_zhexian
    • 5_easy
    • 5_tri

第 5 章

5_cow

实验任务

  有一头母牛,它每年年初生一头小母牛。每头小母牛从第四个年头开始,每年年初也生一头小母牛。请编程实现在第n 年的时候,共有多少头母牛?

数据输入

  第一行1 个正整数n (0

2

输出示例

2

Code

#include 
int main (void)      
{
    short n,i;
    int a[60];
    scanf("%hd",&n);
    if (n<4)
        printf("%d\n",n);
    else
    {
        a[1]=1; a[2]=2; a[3]=3;
        for (i=4;i<=n;i++)
            a[i]=a[i-1]+a[i-3];
        printf("%d\n",a[n]);
    }
    return 0;    
}
1 2 3 4 5 6 7
幼年 0 1 2 3 4 6 9
成年 1 1 1 1 2 3 4
总数 1 2 3 4 6 9 13

- 第一头母牛从第二年开始生小牛
- 小牛出生当年为第一个年头,在第四个年头长为母牛时,马上会生一只小牛
- 规律:a[i]=a[i-1]+a[i-3](i>=4)


5_bee

实验任务

  有一只经过训练的蜜蜂YP 只能爬向右侧相邻的蜂房,不能反向爬行。请编程计算蜜蜂从蜂房a 爬到蜂房b 的可能路线数。其中,蜂房的结构如下所示。
  

数据输入

  两个整数a 和b(0

1 2

输出示例

1

#include      
int main(void)      
{
    short a,b,i;
    __int64 road[55];   //注意使用__int64,以免数据溢出
    scanf("%hd%hd",&a,&b);
    if (b==a || b==a+1) //从a点走到a点和从a点走到a+1点的路径都只有一条
    {
        printf("1\n");
        return 0;
    }
    road[a]=1; road[a+1]=1;
    for (i=a+2;i<=b;i++)
        road[i]=road[i-1]+road[i-2]; //走到i点的路径条数等于走到i-1点的路径条数与走到i-2点的路径条数之和
    printf("%I64d\n",road[b]);
    return 0;    
} 

5_stairs

实验任务

  YP 走进了2 号楼,面临着一个M 级的楼梯,刚开始时他在第一级,若每次只能跨上一级或二级,要走上第M 级,共有多少种走法?

数据输入

  一个正整数n(1<=M<=40),表示楼梯的级数。

数据输出

  输出不同走法的数量。

样例

输入示例

3

输出示例

2

Code

#include 
int main (void)      
{
    short m,i;
    scanf("%hd",&m);
    if (m==1)
        printf("0\n");  //当m==1时,无走法,输出0。
    else if (m==2)
        printf("1\n");
    else
    {
        int a[45];
        a[1]=1; a[2]=1; //初始化,从1级走到3级有一条走法,从2级走到3级也有一条走法。
        for (i=3;i<=m;i++)
            a[i]=a[i-1]+a[i-2];
        printf("%d\n",a[m]);
    }
    return 0;    
}

因为每次只能跨一级或跨两级台阶,所以走到第m级台阶的走法即为走到第m-1级台阶的走法与走到第m-2级台阶的走法之和。


5_zhexian

实验任务

  YP 做过很多直线分割平面的题目,他认为这种题目都太简单了。于是他想到了一个复杂的问题,他想知道的是n 条折线分割平面的最大数目。

数据输入

  一个正整数n(1<=n<=10000),表示折线的数量。

数据输出

  输出平面的最大分割数。

样例

输入示例

2

输出示例

7

Hint


Code

#include 
int main (void)      
{
    short n;
    scanf("%hd",&n);
    printf("%d",2*n*n-n+1);
    return 0;    
}

用折线分割平面,可以将一条折线看作是两条平行的直线在其中一端相连

而一组平行直线在其中一端相连,则原本在这组平行直线两端的不同平面就会连成一个平面,即平面数-1。

=====>

所以对于n条折线分割平面,可以看作由n组平行直线分割平面所得的总面数再减去平行直线的组数n。

所以讨论折线分割平面,可以先讨论n组平行直线分割平面。

而讨论n组平行直线分割平面,可以先讨论n条直线分割平面。

当n=1时,分割最多面数为2

当n=2时,分割最多面数为4

当n=3时,分割最多面数为7

而想要使n条线分割面数最多,关键在于其中任意一条线都要与其余n-1条线相交。

当n=0时,面数为1;当加上第1条线时,与剩下的0条线相交,面数增加0+1=1,总面数为1+1=2,此时n=1。

当n=1时,面数为2;当加上第2条线时,与剩下的1条线相交,面数增加1+1=2,总面数为2+2=4,此时n=2。

当n=2时,面数为4;当加上第3条线时,与剩下的2条线相交,面数增加 2+1=3,总面数为4+3=7,此时n=3。

即当加上第n条线时,与剩下的n-1条线相交,面数就会增加 (n-1)+1=n

所以,从加第1条线到加第n条线,面数的增加量为 1+2+3+...+n=(1+n)×n2 ,又因为在还没有加线时,初始面数为1,所以,用n条线分割平面所得最多的平面数为 1+(1+n)×n2

接下来以三条线分割平面转换成三组平行直线分割平面的情况为例:

  • 用3条线分割平面得到最多7个面

  • 作一条平行于对角线的平行线,因为平行,所以最多与其他2条线相交,面数增加2+1=3

  • 作一条平行于垂直方向线的平行线,其最多与其他3条线相交,面数增加3+1=4

  • 作一条平行于水平方向线的平行线,其最多与其他4条线相交,面数增加4+1=5

因而,可以得出,将3条线转换成3组平行线之后,所分割的面数增加了3+4+5。

其他条数情况则同理可证。

即,对于n条线,将其转换成n组平行线之后,所分割的面数将会增加

(n+0)+(n+1)+(n+2)+...+(n+n1)=n2+n×(n1)2

所以,对于n组平行线,所分割的面数为

1+(1+n)×n2+n×(n1)2=2n2+1

最后,再将n组平行线转化为n条折线,即n组平行线所分割的总面数减去组数n,得n条折线所分割的面数应为

2n2+1n


5_easy

实验任务

  展大浪和天天每一天都在健身房健身,健身房是一个n*m 的网格,每个格子都有一定的数值,代表在这个格子里健身可以消耗的卡路里,展大浪从左上走到右下(只能往右或往下),天天从左下走到右上(只能往右或往上),经过某个格子就可以在这里健身,消耗卡路里。两人在健身房中的某一个格子里必须见一次面(且只能见一次面),在相遇的那个格子里两人都不健身,即两人都不消耗卡路里。由于两人健身的速度未知,因此两人在格子间的移动速度可能不同(也就是说,他们在健身房中各自行走的路线仅能在一个格子中重叠)。求两人消耗卡路里的和的最大值。

数据输入

  第一行2 个正整数n,m(3 ≤ n, m ≤ 1000).
  接下去N 行每行有M 个正整数,第i 行的第j 个数字a[i][j]表示这个格子可以消耗的卡路里值。 (0 ≤ a[i][j] ≤ 10000).

数据输出

  输出两人消耗卡路里的和的最大值。

样例

输入示例

3 3
100 100 100
100 1 100
100 100 100

输出示例

800

Code

#include 
#include 
int a[1010][1010],dp1[1010][1010],dp2[1010][1010],dp3[1010][1010],dp4[1010][1010];

int max (int a,int b)
{
    if (a>=b)
        return a;
    else
        return b;
}

int main (void)      
{
    int i,j,n,m;
    scanf("%d%d",&n,&m);
    for (i=1;i<=n;i++)
        for (j=1;j<=m;j++)
            scanf("%d",&a[i][j]);
    //初始化,赋值为0方便递推
    memset(dp1,0,sizeof(dp1));
    memset(dp2,0,sizeof(dp2));
    memset(dp3,0,sizeof(dp3));
    memset(dp4,0,sizeof(dp4));
    //左上到右下
    for (i=1;i<=n;i++)
        for (j=1;j<=m;j++)
            dp1[i][j]=max(dp1[i-1][j],dp1[i][j-1])+a[i][j];
    //右上到左下
    for (i=1;i<=n;i++)
        for (j=m;j>=1;j--)
            dp2[i][j]=max(dp2[i-1][j],dp2[i][j+1])+a[i][j];
    //左下到右上
    for (i=n;i>=1;i--)
        for (j=1;j<=m;j++)
            dp3[i][j]=max(dp3[i+1][j],dp3[i][j-1])+a[i][j];
    //右下到左上
    for (i=n;i>=1;i--)
        for (j=m;j>=1;j--)
            dp4[i][j]=max(dp4[i+1][j],dp4[i][j+1])+a[i][j];

    int ans=-1;
    for (i=2;i<=n-1;i++)    //边界点不可枚举
    {
        for (j=2;j<=m-1;j++)
        {   //两种相交情况
            ans=max(dp1[i-1][j]+dp2[i][j+1]+dp3[i][j-1]+dp4[i+1][j],ans);
            ans=max(dp1[i][j-1]+dp2[i-1][j]+dp3[i+1][j]+dp4[i][j+1],ans);
        }
    }
    printf("%d\n",ans);
    return 0;
}

寻找到某个点的消耗卡路里最大的路径,可以用递推的方法,从起点推到目标点。

以从左上角作起点为例,动规方程为:

dp1[i][j]=max(dp1[i-1][j],dp1[i][j-1])+a[i][j]

即从目标点的上方和左方选取一条消耗卡路里最大的路径。

以此类推,其他情况同理。

所以,枚举所有可能点,找出四个角到此点的消耗卡路里值最大的路径,求和,取最大值。

因为两人的路径只能有一个相交点,所以相交点不可能在边界,且相交情况只有以下两种

所以,找出四个区域里的最优路径,求和,比较两种情况的两者大小,最后再与其他点比较,取最大值。

预处理:

不必每次枚举一个点都重新递推一遍四个区域的最优路径,而是可先做预处理,求出从 左上到右下、右上到左下,左下到右上,右下到左上 四个方向上,到各个点所能取得的最大消耗卡路里的值,即最优路径。

这样处理完毕之后,只需要枚举所有可能点,再取可能点的四个方向上的值进行求和(分上图两种情况),最后取最大值即可。


5_tri

实验任务

  在讲述DP 算法的时候,一个经典的例子就是数塔问题,它是这样描述的: 有如下所示的数塔,要求从顶层走到底层,若每一步只能走到相邻的结点,则经过的结点的数字之和最大是多少?

已经告诉你了,这是个DP的题目,你能AC吗?

数据输入

  第一行是一个整数N(1 <= N <= 100),表示数塔的高度,接下来用N 行数字表示数塔,其中第i 行有个i 个整数,且所有的整数均在区间(0,99)内。

数据输出

  输出数字之和最大是多少。

样例

输入示例

5
7
3 8
8 1 0
2 7 4 4
4 5 2 6 5

输出示例

30

Code

#include 
#include 

int max (int a,int b)
{
    if (a>=b)
        return a;
    else
        return b;
}

int main (void)      
{
    int i,j,n;
    scanf("%d",&n);

    int dp[105][105];
    memset(dp,0,sizeof(dp));
    for (i=1;i<=n;i++)
        for (j=1;j<=i;j++)
        {
            int t;
            scanf("%d",&t);
            dp[i][j]=max(dp[i-1][j-1],dp[i-1][j])+t;
        }
    int ans=-1;
    for (i=1;i<=n;i++)
        ans=max(ans,dp[n][i]);
    printf("%d\n",ans);
    return 0;
}

目标位置的最大值,即最优路径,为目标位置左上方和正上方中,值最大的那个,再加上本位置的值,递推即可。

动规方程为:

dp[i][j]=max(dp[i-1][j-1],dp[i-1][j])+t

t 为位置 [i][j] 上的值。


你可能感兴趣的:(题解)