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

题目链接
题目大意:给\(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)当我们选的当前元素不是第二段的第一个时:
  
  这时候相当于在之前已经选好的最大值的基础上加上当前元素的值。
  所以说,\(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;
}
posted @ 2020-04-07 15:49  shuitiangong  阅读(116)  评论(0编辑  收藏  举报