纯递归
例题:P1115 最大子段和
题目描述
给出一段序列,选出其中连续且非空的一段使得这段和最大。
输入输出格式
输入格式:
第一行是一个正整数N,表示了序列的长度。
第二行包含N个绝对值不大于1000010000的整数A_i ,描述了这段序列。
输出格式:
一个整数,为最大的子段和是多少。子段的最小长度为11。
输入输出样例
输入样例#1:
7
2 -4 3 -1 2 -4 3
输出样例#1:
4
说明
【样例说明】
2,-4,3,-1,2,-4,3中,最大的子段和为4,该子段为3,-1,2.
【数据规模与约定】
对于40%40%的数据,有N ≤ 2000。
对于100%100%的数据,有N ≤ 200000。
代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int qq[200020],ss[200020];
int main()
{
int n,i;
scanf("%d",&n);
for(i=1;i<=n;i++)
scanf("%d",&qq[i]);
ss[0]=0;//s【200020】代表到i时的最大子串
for(i=1;i<=n;i++)
{
ss[i]=max(qq[i],ss[i-1]+qq[i]);
}
sort(ss+1,ss+n+1);
printf("%d\n",ss[n]);
return 0;
}
P2642 双子序列最大和
题目描述
给定一个长度为n的整数序列,要求从中选出两个连续子序列,使得这两个连续子序列的序列和之和最大,最终只需输出最大和。一个连续子序列的和为该子序列中所有数之和。每个连续子序列的最小长度为1,并且两个连续子序列之间至少间隔一个数。
输入输出格式
输入格式:
第一行是一个整数表示n。
第二行是n个整数表示整数序列。
输出格式:
一个数,两个连续子序列的序列和之和。
输入输出样例
输入样例#1:
5
83 223 -13 1331 -935
输出样例#1:
1637
输入样例#2:
3
83 223 -13
输出样例#2:
70
说明
对于30%的数据N<=100。
对于60%的数据有N<=10000。
对于100%的数据有N<=1000000。
数据保证运算过程不会超过long long(int64)。
代码:
#include<cstdio>
#include<iostream>
#include<cmath>
#include<cstring>
#include<iostream>
using namespace std;
long long int qq[1000010],ansl[1000010],ansr[1000010],fl[1000010],fr[1000010];
long long int n,ans;
int main()
{
long long int i,j;
scanf("%lld",&n);
for(i=1;i<=n;i++)
scanf("%lld",&qq[i]);
ansl[1]=fl[1]=qq[1];
ansr[n]=fr[n]=qq[n];
for(i=2;i<=n;i++)
{
fl[i]=max(qq[i],fl[i-1]+qq[i]);
ansl[i]=max(ansl[i-1],fl[i]);
//ansl[i]代表从左到i的最大子序列(不一定到i)
}
for(i=n-1;i>=1;i--)
{
fr[i]=max(qq[i],fr[i+1]+qq[i]);
ansr[i]=max(ansr[i+1],fr[i]);
//ansr[i]代表从右到i的最大子序列(不一定到i)
}
ans=ansl[1]+ansr[3];
for(i=2;i<=n-2;i++)
ans=max(ans,ansl[i]+ansr[i+2]);
printf("%lld\n",ans);
return 0;
}
例题:P1968 美元汇率
题目描述
在以后的若干天里戴维将学习美元与德国马克的汇率。编写程序帮助戴维何时应买或卖马克或美元,使他从100美元开始,最后能获得最高可能的价值。
输入输出格式
输入格式:
输入文件的第一行是一个自然数N,1≤N≤100,表示戴维学习汇率的天数。
接下来的N行中每行是一个自然数A,1≤A≤1000。第i+1行的A表示预先知道的第i+1天的平均汇率,在这一天中,戴维既能用100美元买A马克也能用A马克购买100美元。
输出格式:
输出文件的第一行也是唯一的一行应输出要求的钱数(单位为美元,保留两位小数)。
注意:考虑到实数算术运算中进位的误差,结果在正确结果0.05美元范围内的被认为是正确的,戴维必须在最后一天结束之前将他的钱都换成美元。
输入输出样例
输入样例#1:
5
400
300
500
300
250
输出样例#1:
266.67
说明
样例解释
Day 1 … changing 100.0000 美元= 400.0000 马克
Day 2 … changing 400.0000 马克= 133.3333 美元
Day 3 … changing 133.3333 美元= 666.6666 马克
Day 5 … changing 666.6666 马克= 266.6666 美元
代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<string>
using namespace std;
int main()
{
double dd,ma,t;//由于dp1的值变化后,无法计算dp2,所以要用临时变量记录未作处理前的dp1
int n,i,a;
scanf("%d",&n);
dd=100.0;
ma=0.0;
for(i=1;i<=n;i++)
{
scanf("%d",&a);
t=dd;
dd=max(dd,ma/a*100);//将马克换成美元进行比较
ma=max(ma,t/100*a);//将美元换成马克进行比较
}
printf("%.2lf\n",dd);
return 0;
}
滚动数组
例题:P2646 数数zzy
题目描述
zzy自从数学考试连续跪掉之后,上数学课就从来不认真听了(事实上他以前也不认真听)。于是他开始在草稿纸上写写画画,比如写一串奇怪的字符串。然后他决定理♂性♂愉♂悦♂一下:统计这串字符串当中共有多少个为“zzy”的子序列(注意是子序列而非子串)。但是由于他写的字符串实在是太长啦,而且他是个超级大蒟蒻,根本就数不过来。所以他决定请求你这个超级大神犇的帮助。你可以帮帮他吗?
输入输出格式
输入格式:
一行仅含小写字母的字符串。
输出格式:
一行,一个非负整数,表示输入的字符串中为“zzy”的子序列的个数。
输入输出样例
输入样例#1:
zlzhy
输出样例#1:
1
说明
70%的数据满足:1<=n<=100。
100%的数据满足:1<=n<=1000000。
n表示字符串的长度
数据保证答案不超过2^63-1
代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<string>
using namespace std;
int main()
{
char ch[1000010];
long long int k,j=0,i,n,a=0,b=0,ans=0;
int qq[1000010];
int flag=0;
scanf("%s",ch);
n=strlen(ch);
for(i=0;i<n;i++)
{
if(ch[i]=='z') a++;
else if(ch[i]=='y') b++;
}
a/=2;
for(i=0;i<n;i++)
{
if(ch[i]=='z')
{
j++;
qq[j]=b;
}
else if(ch[i]=='y')
b--;
}
for(k=1;k<=j;k++)
ans=ans+qq[k]*(k-1);
printf("%lld\n",ans);
return 0;
}
倒着DP
例题:P2374 搬运工
题目描述
前些天,高一的新同学来了,他便像往常一样兜售他的书,经过一番口舌,同学们决定买他的书,但是陈老师桌上的书有三堆,每一堆都有厚厚的一叠,他要想个办法用最轻松的方式把书拿下来给同学们。但是你想逗一下陈老师,于是你设计一个最累的方式给他。若告诉你这三堆分别有i,j,k本书,以及每堆从下到上书的质量,每次取书只能从任一堆的最上面取,那么请你设计一个方案,让他花最大的力气取下所有的书。
显然,每次取书陈老师的体力消耗都会加大,这里用体力系数代表,取下第一本书时,体力系数为1,第二本书时体力系数为2,依次类推,而每次体力消耗值则为体力系数与书的重量之积。
输入输出格式
输入格式:
第一行3个整数,分别为三堆书的数量i,j,k
第二行至第四行分别为每堆由下至上的书本重量
输出格式:
输出最累方式的体力消耗总值。
输入输出样例
输入样例#1:
3 2 4
2 3 2
1 5
9 8 7 4
输出样例#1:
257
说明
对于50%的数据有0≤i,j,k<10;
对于100%的数据有0≤i,j,k<100
最后输出的体力消耗总值在int范围内
代码:
设f[i][j][k]表示三堆分别已经取了i,j,k本书的最优价值,方程是f[i][j][k]=max{f[i-1][j][k]+pa[i],f[i][j-1][k]+pb[j],f[i][j][k-1]+p*c[k]} p 就是劳累程度,p=i+j+k.
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<string>
#include<cmath>
#include<iostream>
using namespace std;
int a,b,c,x[110],y[110],z[110],ss[110][110][110];
int main()
{
int i,j,k,s,sum;
scanf("%d%d%d",&a,&b,&c);
sum=a+b+c;
memset(ss,0,sizeof(ss));
x[0]=y[0]=z[0]=0;
for(i=1;i<=a;i++)
scanf("%d",&x[i]);
for(j=1;j<=b;j++)
scanf("%d",&y[j]);
for(k=1;k<=c;k++)
scanf("%d",&z[k]);
for(i=a;i>=0;i--)
for(j=b;j>=0;j--)
for(k=c;k>=0;k--)
{
s=sum-i-j-k+1;
if(i) ss[i-1][j][k]=ss[i][j][k]+s*x[i];
if(j) ss[i][j-1][k]=max(ss[i][j-1][k],ss[i][j][k]+s*y[j]);
if(k) ss[i][j][k-1]=max(ss[i][j][k-1],ss[i][j][k]+s*z[k]);
}
printf("%d\n",ss[0][0][0]);
return 0;
}
隐含地DP
例题:P1057 传球游戏
题目描述
上体育课的时候,小蛮的老师经常带着同学们一起做游戏。这次,老师带着同学们一起做传球游戏。
游戏规则是这样的:n个同学站成一个圆圈,其中的一个同学手里拿着一个球,当老师吹哨子时开始传球,每个同学可以把球传给自己左右的两个同学中的一个(左右任意),当老师再次吹哨子时,传球停止,此时,拿着球没有传出去的那个同学就是败者,要给大家表演一个节目。
聪明的小蛮提出一个有趣的问题:有多少种不同的传球方法可以使得从小蛮手里开始传的球,传了m次以后,又回到小蛮手里。两种传球方法被视作不同的方法,当且仅当这两种方法中,接到球的同学按接球顺序组成的序列是不同的。比如有三个同学1号、2号、3号,并假设小蛮为1号,球传了3次回到小蛮手里的方式有1->2->3->1和1->3->2->1,共2种。
输入输出格式
输入格式:
一行,有两个用空格隔开的整数n,m(3≤n≤30,1≤m≤30)。
输出格式:
11个整数,表示符合题意的方法数。
输入输出样例
输入样例#1:
3 3
输出样例#1:
2
说明
40%的数据满足:3≤n≤30,1≤m≤20
100%的数据满足:3≤n≤30,1≤m≤30
代码:
设f[i][k]表示经过k次传到编号为i的人手中的方案数,传到i号同 学的球只能来自于i的左边一个同学和右边一个同学,这两个同学的编号分别是i-1和i+1,所以可以得到以下的递推公式:
f[i][k]=f[i-1][k-1]+f[i+1][k-1],(i=1或n时,需单独处理)。
边界条件:f[1][0]=1(特别注意);结果在f[1][m]中。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<string>
#include<algorithm>
using namespace std;
int n,qq[50][50],m;
int main()
{
int i,j;
scanf("%d%d",&n,&m);
memset(qq,0,sizeof(qq));
qq[1][0]=1;
for(i=1;i<=m;i++)
{
qq[1][i]=qq[2][i-1]+qq[n][i-1];
for(j=2;j<=n;j++)
{
qq[j][i]=qq[j-1][i-1]+qq[j%n+1][i-1];
}
}
printf("%d\n",qq[1][m]);
return 0;
}