首页 > 其他 > 详细

HDU - 1024 - Max Sum Plus Plus(连续子段和++)

时间:2020-04-07 16:07:38      阅读:54      评论:0      收藏:0      [点我收藏+]

题目链接 题目大意:给$n$个数,把他们划成$m$个连续子段,求这个$m$个连续子段的和的最大值。子段与子段之间不交叉,可以不连续。 ??这题如果直接做的话不太好做,我们先考虑一下$m=1$的情况。设$dp$数组为$dp[i][j]$,$i$表示划成$i$段的最大子段和,$j$表示当前第$j$个元素,数组$a$存的是$m$个元素的值。 ??1.$m=1$时,就相当于最经典的连续子段和问题了,状态转移方程就是$dp[1][j] = max(a[j], dp[1][j-1]+a[j])$。 ??2.$m=2$时,假设我们从$1$到$j(j\in [2,n])$之间选两个连续子段的话,分两种情况,假设选择的最左边的子段为蓝色,中间的为黄色,当前数组的元素为红色。 ??(1)当我们选的当前元素为独立一段时: ??技术分享图片。 ??我们只要选一个在$j$之前的一个最大子段和再加上当前元素即可。$j$之前的一个最大子段和我们遍历之前求出的$m=1$的时候最大子段和就能求出来。 ??(2)当我们之前已经选好了两段,当前的元素与第二段相连时(比如说发生上一种情况之后的下一个元素): ??技术分享图片。 ??这时候相当于在之前已经选好的最大值的基础上加上当前元素的值。 ??(3)所以说,$m=2$时的状态转移方程为$dp[2][j] = max(dp[2][j-1], pre(1, j-1)) + a[j]$,$pre()\(用来求出\)[1,j-1]$之间的最大子段和。 ??3.$m=3$时, 与$m=2$时的情况类似,我们可以把它拆成已经选好了$2$个子段,求第$3$个子段的情况。 ??4.从上面的过程中我们可以总结出来状态转移方程为$dp[i][j] = max(dp[i][j-1], pre(i-1, j-1)) + a[j], \ pre(i-1, j-1) = max(dp[i-1][k])(k\in [i-1, j-1])$

const int maxn = 1e6+10;
int n, m;
ll dp[10][maxn], arr[maxn];
ll pre(int floor, int r) {
    ll maxx = -LLONG_MAX;
    for (int i = floor; i<=r; ++i)
        maxx = max(maxx, dp[floor][i]);
    return maxx;
}
int main(void) {
    while(~scanf("%d%d", &n, &m) && m) {
        for (int i = 1; i<=m; ++i) scanf("%lld", &arr[i]);
        ll ans = -LLONG_MAX;
        for (int i = 1; i<=n; ++i)
            for (int j = i; j<=m; ++j) {
                dp[i][j] = max(dp[i][j-1], pre(i-1, j-1)) + arr[j];
                ans = max(ans, dp[i][j]);
            }
        printf("%lld\n", ans);
    }
    return 0;
}

??然后你就TLE了(笑) ??所以说我们还需要对时间进行优化,好像空间也得优化一下不然可能会MLE。 ??首先对时间进行优化,我们可以很容易发现我们的时间复杂度是$O(mn^2)$的,但是如果我们优化一下求$pre$的话就能把时间复杂度缩小到$O(mn)$, 我们只需要用一个数组,将上一组元素的每个元素之前最大的元素保存下来就行了。 ??然后就是对空间优化,很明显$dp[i]$的值之和$dp[i-1]$有关,所以我们没有必要开一个二维数组,只用一维数组就行了,这种优化背包问题之类的$dp$问题已经讲的很多了就不多少了。

const int maxn = 1e6+10;
int n, m;
ll dp[maxn], arr[maxn], pre[maxn];
int main(void) {
    while(~scanf("%d%d", &n, &m) && m) {
        for (int i = 1; i<=m; ++i) {
            scanf("%lld", &arr[i]);
            pre[i] = 0;
        }
        ll tmp;
        for (int i = 1; i<=n; ++i) {
            tmp = -LLONG_MAX;
            for (int j = i; j<=m; ++j) {
                dp[j] = max(dp[j-1], pre[j-1]) + arr[j];
                pre[j-1] = tmp;
                tmp = max(tmp, dp[j]);
            }
        }
        printf("%lld\n", tmp);
    }
    return 0;
}

HDU - 1024 - Max Sum Plus Plus(连续子段和++)

原文:https://www.cnblogs.com/shuitiangong/p/12653818.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!