dp训练计划——hdu1024最大子段和

题目链接:https://vjudge.net/problem/HDU-1024

题目大意:给你一个长度为n的序列,让你求出把这个序列分为m个不相交子段的最大和。

题解:从今天开始重新刷一起没写完的dp了,开始自闭之旅。

首先说一下状态:

dp[i][j]表示在前j个数中分成i个子段的最大值,并且最大值以第j项结尾

状态转移方程:

dp[i][j]=max(dp[i][j-1]+a[j],dp[i-1][k]+a[j])    (0<=k<=j)

dp[i][j-1]+a[j] 表示在前j-1个数中分成i个字段的最大值,最大值以第j-1项结尾,

那就我们就可以在最后一组后面再加上一个a[j]。

dp[i-1][k]+a[j] 表示在前k个数中分成i-1个字段的最大值,a[j]单独成为第i组

状态转移方程不是特别难想,不过要清楚的明白这个递推的过程,这样下面优化的时候才能看懂,这也是关键。

不难发现我们这样写的时间复杂度是大致为O(n^3),当n和m同阶时。

下面我们考虑怎么优化,在递推时我们可以发现,每一项都是由它的左上方的最大值以及左边的值来确定,那么我们就可以每一存一下前缀的最大值,方便下一行来用,其实就是再开一个MAX数组来记录上一行的前缀最大值。

其实还有更优化的方法,不过就涉及到一些贪心的处理,可以参考51nod的最大子段和V

如果需要比较详细的分析的话可以参考这篇文章:最大字段和

trick:这个题O(nm)是可以过的,而且最后答案不是dp[m][n],想想状态的定义就知道为什么了。

代码实现:

#pragma GCC optimize(2)
#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 + 7;
long long a[N];
long long dp[N], MAX[N], MAXX;
/*
dp[i][j]表示在前j个数中分成i个子段的最大值,并且最大值以a[j]结尾
dp[i][j]=max(dp[i][j-1]+a[j],dp[i-1][k]+a[j])    (0<=k<=j)
dp[i][j-1]+a[j] 表示在前j-1个数中分成i个字段的最大值,最大值以a[j-1]结尾,
那么就我们在可以最后一组后面加上一个a[j]。
dp[i-1][k]+a[j] 表示在前k个数中分成i-1个字段的最大值,a[j]单独成为第i组
*/
int main()
{
    int n, m;
    while (~scanf("%d%d", &m, &n))
    {
        for (int i = 1; i <= n; i++)
            MAX[i] = dp[i] = 0;
        int cnt = 0;
        for (int i = 1; i <= n; i++)
            scanf("%lld", &a[i]);
        for (int i = 1; i <= m; i++)
        {
            MAXX = -0x3f3f3f3f3f3f;
            for (int j = i; j <= n; j++)
            {
                dp[j] = max(dp[j - 1] + a[j], MAX[j - 1] + a[j]);
                MAX[j - 1] = MAXX;
                MAXX = max(MAXX, dp[j]);
            }
        }
        printf("%lld\n", MAXX);
    }
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值