HDU - 1024 Max Sum Plus Plus(m段最大和+滚动数组优化)

题目链接https://vjudge.net/contest/347398#problem/E
在这里插入图片描述
翻译
输入两个数m和n,接下来有n个数。
sum(i,j)=a[i]+a[i+1]+…+a[j]的和。
求找出m个(i,j)使得 sum(i1, j1) + sum(i2, j2) + sum(i3, j3) + … + sum(im, jm) 最大。
这m个区间不能相交
对于2 6 -1 4 -2 3 -2 3这组数据,输出8,选择的区间为4 -2 3和3这两个区间。
分析
w[i][j]: 前 j 个数分为 i 段,第 j 个数必须选可以分成的情况:
1 第 j 个数单独为1段
2. 第 j 个数与前面的数连一块。
w[i][j] = max(b[i-1][j-1], w[i][j-1]) + a[j]
b[i][j]前 j 个数分为 i 段,第 j 个数可选可不选可以分成的情况:
1.选第 j 个数;
2.不选第 j 个数。
b[i][j] = max(b[i][j-1], w[i][j])

为什么可以利用滚动数组优化
仔细观察递归式
w[i][j] = max(b[i-1][j-1], w[i][j-1]) + a[j]
b[i][j] = max(b[i][j-1], w[i][j])

发现,对于取 i 段,w[i][j] 只与 b[i-1][j-1] 和 w[i][j-1]有关, 与之前的那一些项没有关系
代码

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=1000010;
typedef long long LL;
int n,m;
LL sum[N],dp[2][N],w[N];
int main()
{
    while(~scanf("%d%d",&m,&n))
    {
        sum[0]=0;
        for(int i=1; i<=n; i++)
        {
            LL x;
            scanf("%lld",&x);
            sum[i]=sum[i-1]+x;
            dp[0][i]=0;/*从前i个元素中取0,最大值为0*/
        }
        for(int i=1; i<=m; i++)
        {
            for(int j=i; j<=n; j++)/*分成i组,最少需要i个数*/
            {
                if(i==j)
                    dp[i&1][j]=w[j]=sum[j];
                else
                {
                    w[j]=max(dp[(i-1)&1][j-1],w[j-1])+sum[j]-sum[j-1];
                    dp[i&1][j]=max(dp[i&1][j-1],w[j]);
                }
            }
        }
        printf("%lld\n",dp[m&1][n]);
    }
    return 0;
}

在这里插入图片描述

发布了165 篇原创文章 · 获赞 6 · 访问量 5051

猜你喜欢

转载自blog.csdn.net/lylzsx20172018/article/details/103483213