实验任务
有一头母牛,它每年年初生一头小母牛。每头小母牛从第四个年头开始,每年年初也生一头小母牛。请编程实现在第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)
实验任务
有一只经过训练的蜜蜂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;
}
实验任务
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级台阶的走法之和。
实验任务
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+n−1)=n2+n×(n−1)2
所以,对于n组平行线,所分割的面数为
1+(1+n)×n2+n×(n−1)2=2n2+1
最后,再将n组平行线转化为n条折线,即n组平行线所分割的总面数减去组数n,得n条折线所分割的面数应为
2n2+1−n
实验任务
展大浪和天天每一天都在健身房健身,健身房是一个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]
即从目标点的上方和左方选取一条消耗卡路里最大的路径。
以此类推,其他情况同理。
所以,枚举所有可能点,找出四个角到此点的消耗卡路里值最大的路径,求和,取最大值。
因为两人的路径只能有一个相交点,所以相交点不可能在边界,且相交情况只有以下两种
所以,找出四个区域里的最优路径,求和,比较两种情况的两者大小,最后再与其他点比较,取最大值。
预处理:
不必每次枚举一个点都重新递推一遍四个区域的最优路径,而是可先做预处理,求出从 左上到右下、右上到左下,左下到右上,右下到左上 四个方向上,到各个点所能取得的最大消耗卡路里的值,即最优路径。
这样处理完毕之后,只需要枚举所有可能点,再取可能点的四个方向上的值进行求和(分上图两种情况),最后取最大值即可。
实验任务
在讲述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] 上的值。