51nod 1052[最大子段和]【DP】

来源:互联网 发布:excel矩阵相乘value 编辑:程序博客网 时间:2024/06/02 05:26

Description

N个整数组成的序列a[1],a[2],a[3],…,a[n],将这N个数划分为互不相交的M个子段,并且这M个子段的和是最大的。如果M >= N个数中正数的个数,那么输出所有正数的和。

例如:-2 11 -4 13 -5 6 -2,分为2段,11 -4 13一段,6一段,和为26。

题解

首先,题目的意思有点绕,这里的答案其实是可以选1..m个子段的最优解。

那么,我们定义f[i][j]表示第i位,当前总共选了j段的最优解,那么有转移方程f[i][j]=max(f[i1][j]+a[i],f[k][j1]+a[i]),这里的k是所有[1,i1]的数,这样,我们就得到了一个N3的算法,优化到N2只要开一个g[i]表示所有f[k][i]的最大值就可以。最后由于空间限制比较毒,所以还要开滚动数组。

代码

#include<cstdio>#include<cstring>#include<algorithm>#define maxn 5006#define LL long longusing namespace std;inline char nc(){    static char buf[100000],*i=buf,*j=buf;    return i==j&&(j=(i=buf)+fread(buf,1,100000,stdin),i==j)?EOF:*i++;}inline int _read(){    char ch=nc();int sum=0,p=1;    while(ch!='-'&&!(ch>='0'&&ch<='9'))ch=nc();    if(ch=='-')p=-1,ch=nc();    while(ch>='0'&&ch<='9')sum=sum*10+ch-48,ch=nc();    return sum*p;}int n,m,a[maxn];LL ans,sum[maxn],g[maxn],f[2][maxn];int main(){    freopen("sequence.in","r",stdin);    freopen("sequence.out","w",stdout);    n=_read();m=_read();    for(int i=1;i<=n;i++)a[i]=_read();    memset(f,192,sizeof(f));memset(g,192,sizeof(g));    g[0]=0;    for(int i=1;i<=n;i++)     for(int j=m;j>=1;j--){        f[i&1][j]=max(g[j-1]+a[i],f[1-(i&1)][j]+a[i]);        g[j]=max(g[j],f[i&1][j]);        ans=max(ans,f[i&1][j]);     }    printf("%lld",ans);    return 0;}
原创粉丝点击