HDU 1024 Max Sum Plus Plus 动态规划

Problem Description

Now I think you have got an AC in Ignatius.L’s “Max Sum” problem. To be a brave ACMer, we always challenge ourselves to more difficult problems. Now you are faced with a more difficult problem.

Given a consecutive number sequence S 1 , S 2 , S 3 , S 4 . . . S x , . . . S n   ( 1 ≤ x ≤ n ≤ 1 , 000 , 000 , − 32768 ≤ S x ≤ 32767 ) S_{1}, S_{2}, S_{3}, S_{4} ... S x, ... S_{n} \space (1 \le x \le n \le 1,000,000, -32768 \le S_{x} \le 32767) S1,S2,S3,S4...Sx,...Sn (1xn1,000,000,32768Sx32767).
We define a function
s u m ( i , j ) = S i + . . . + S j   ( 1 ≤ i ≤ j ≤ n ) sum(i, j) = S_{i} + ... + S_{j} \space (1 \le i \le j \le n) sum(i,j)=Si+...+Sj (1ijn)

Now given an integer m (m > 0), your task is to find m pairs of i and j which make
s u m ( i 1 , j 1 ) + s u m ( i 2 , j 2 ) + s u m ( i 3 , j 3 ) + . . . + s u m ( i m , j m ) sum(i_{1}, j_{1}) + sum(i_{2}, j_{2}) + sum(i_{3}, j_{3}) + ... + sum(i_{m}, j_{m}) sum(i1,j1)+sum(i2,j2)+sum(i3,j3)+...+sum(im,jm)
maximal ( i x ≤ i y ≤ j x   o r   i x ≤ j y ≤ j x i_{x} \le i_{y} \le j_{x} \space or \space i_{x} \le j_{y} \le j_{x} ixiyjx or ixjyjx is not allowed).

But I’m lazy, I don’t want to write a special-judge module, so you don’t have to output m pairs of i and j, just output the maximal summation of s u m ( i x , j x )   ( 1 ≤ x ≤ m ) sum(i_{x}, j_{x}) \space (1 \le x \le m) sum(ix,jx) (1xm) instead.

Input

Each test case will begin with two integers m and n, followed by n integers S 1 , S 2 , S 3 , S 4 . . . S x , . . . S n S_{1}, S_{2}, S_{3}, S_{4} ... S x, ... S_{n} S1,S2,S3,S4...Sx,...Sn
Process to the end of file.

Output

Output the maximal summation described above in one line.

Sample Input

1 3 1 2 3
2 6 -1 4 -2 3 -2 3

Sample Output

6
8
\newline
\newline
\newline
\newline

思路

题目概括一下就是给定一个数组,要从中取出 m 个互不重叠的子数组,并且使子数组的和的总和最大。

方向是往动态规划考虑,怎么定义状态是个问题。

状态定义1

定义 d p [ i ] [ j ] dp[i][j] dp[i][j] 为数组前 i i i 个元素中取 j j j 个子数组,总和的最大值。

状态转移方程为:
d p [ i ] [ j ] = m a x ( d p [ i − 1 ] [ j ] , m a x 0 ≤ k < i   d p [ k ] [ j − 1 ] + s u m ( i − 1 ) − s u m ( k − 1 ) ) dp[i][j] = max(dp[i - 1][j], max_{0 \le k < i} \space dp[k][j - 1] + sum(i - 1) - sum(k - 1)) dp[i][j]=max(dp[i1][j],max0k<i dp[k][j1]+sum(i1)sum(k1))

\newline
其中 s u m ( i ) sum(i) sum(i) 为前缀和,我们可以用一个变量维护前缀 m a x 0 ≤ k < i   ( d p [ k ] [ j − 1 ] − s u m ( k − 1 ) ) max_{0 \le k < i} \space (dp[k][j - 1] - sum(k - 1)) max0k<i (dp[k][j1]sum(k1)) 最大值。

另外可以用滚动数组来减少一维空间使用量。算法时间复杂度为 O ( m n ) O(mn) O(mn),空间使用为 O ( n ) O(n) O(n)

import java.util.Arrays;
import java.util.Scanner;

public class Main {
    private long solve(long[] arr, int m) {
        int n = arr.length;
        long[] prev = new long[n + 1];
        long[] sum = new long[n];
        for (int i = 0; i < n; ++i) {
            sum[i] = arr[i] + (i > 0 ? sum[i - 1] : 0);
        }
        for (int i = 1; i <= m; ++i) {
            long[] next = new long[1 + n];
            long max = prev[i - 1] - (i > 1 ? sum[i - 2] : 0);
            /**
             * The next[] array value is only meaningful when j >= i.
             * The prev[] array value is only meaningful when j >= i - 1.
             */
            for (int j = i; j <= n; ++j) {
                next[j] = max + sum[j - 1];
                // Only when j > i then next[j - 1] is meaningful.
                if (j > i) {
                    next[j] = Math.max(next[j], next[j - 1]);
                }
                max = Math.max(max, prev[j] - sum[j - 1]);
            }
            prev = next;
        }
        return prev[n];
    }

    public static void main(String[] args) {
        Main main = new Main();
        Scanner sc = new Scanner(System.in);
        while (sc.hasNext()) {
            int m = sc.nextInt();
            int n = sc.nextInt();
            long[] arr = new long[n];
            for (int i = 0; i < n; ++i) {
                arr[i] = sc.nextLong();
            }
            System.out.println(main.solve(arr, m));
        }
    }
}

\newline
\newline
\newline
\newline

状态定义2

前一个方法需要用一个变量维护前缀 m a x 0 ≤ k < i   ( d p [ k ] [ j − 1 ] − s u m ( k − 1 ) ) max_{0 \le k < i} \space (dp[k][j - 1] - sum(k - 1)) max0k<i (dp[k][j1]sum(k1)) 最大值,看上去就比较复杂,很 tricky 而且易错。造出这种情况的原因是状态定义的不够好,试试在前一个方法的状态定力里面附加一个额外条件:必须以 A [ i − 1 ] A[i - 1] A[i1] 结尾。

定义状态 d p [ i ] [ j ] dp[i][j] dp[i][j] 为从数组的前 i i i 个元素中取 j j j 个子数组,且最后一个子数组以元素 A [ i − 1 ] A[i - 1] A[i1] 结尾。这样最后的结果为
m a x ( d p [ m ] [ 0 ] , d p [ m ] [ 1 ] , . . . d p [ m ] [ n − 1 ] ) max(dp[m][0], dp[m][1], ... dp[m][n - 1]) max(dp[m][0],dp[m][1],...dp[m][n1])

状态转移方程为:
d p [ i ] [ j ] = m a x ( d p [ i − 1 ] [ j ] ,   m a x 0 ≤ k < i   d p [ k ] [ j − 1 ] ) + A [ i − 1 ] dp[i][j] = max(dp[i - 1][j], \space max_{0 \le k < i} \space dp[k][j - 1] ) + A[i - 1] dp[i][j]=max(dp[i1][j], max0k<i dp[k][j1])+A[i1]

这里 d p [ i − 1 ] [ j ] dp[i - 1][j] dp[i1][j] 是指元素 A [ i − 1 ] A[i - 1] A[i1] 和前面一个元素合并属于组同一个子数组的情况。 m a x 0 ≤ k < i   d p [ k ] [ j − 1 ] + A [ i − 1 ] max_{0 \le k < i} \space dp[k][j - 1] + A[i - 1] max0k<i dp[k][j1]+A[i1] 是元素 A [ i − 1 ] A[i - 1] A[i1] 另起炉灶作为一个新的子数组。这样我们只需维护一个前缀 m a x 0 ≤ k < i   d p [ k ] [ j − 1 ] max_{0 \le k < i} \space dp[k][j - 1] max0k<i dp[k][j1] 的最大值,比上一个方法简单很多。

import java.util.Arrays;
import java.util.Scanner;

public class Main {
    private long solve(long[] arr, int m) {
        int n = arr.length;
        long[] prev = new long[n + 1];
        for (int i = 1; i <= m; ++i) {
            long[] next = new long[n + 1];
            long max = prev[i - 1];
            for (int j = i; j <= n; ++j) {
                next[j] = max + arr[j - 1];
                if (j > i) {
                    next[j] = Math.max(next[j], arr[j - 1] + next[j - 1]);
                }
                max = Math.max(max, prev[j]);
            }
            prev = next;
        }
        long ans = Long.MIN_VALUE;
        // array is only meaningful from i >= m.
        for (int i = m; i <= n; ++i) {
            ans = Math.max(ans, prev[i]);
        }
        return ans;
    }

    public static void main(String[] args) {
        Main main = new Main();
        Scanner sc = new Scanner(System.in);
        while (sc.hasNext()) {
            int m = sc.nextInt();
            int n = sc.nextInt();
            long[] arr = new long[n];
            for (int i = 0; i < n; ++i) {
                arr[i] = sc.nextLong();
            }
            System.out.println(main.solve(arr, m));
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

aliengod

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值