寒假笔记·递归数组

纯递归

例题: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;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值