HDU 1024

因为我笨,思考了好久,看了好多博客,所以希望写的细一些= =等我忘了我是怎么想的,能回来一眼就看懂。

题意:

给一个n个数的整数序列,要你将其分为不相交的任意段,并取其中m段,求m段和的最大值。

例如: 

1 2 3  取1段, 即为“1 2 3”,和为6

-1 4 -2 3 -2 3  取两段,即为“4” ,“3 -2 3”,和为8

解题思路:

dp[i][j]在前j个数中,分i组,并选取第j个数,取得的最大值。

递推方程:dp[i][j] = max(dp[i-1][1 ~ (j - 1)] + a[j], dp[i][j - 1]),其中dp[i-1][1 ~ (j - 1)]第j个数之前的数,分为i-1段,第j个数单独分为1段, dp[i][j - 1]第j个数连到最后一段

因为数据很大,用二位数组会超内存,所以通过滚动数组,只需一维空间,节省内存。

通过观察递推公式dp[i][j] = max( max(dp[i-1][1 ~ (j - 1)]) + a[j],  dp[i][j - 1]),可知dp[i][j]只与dp[i-1][...]以及dp[i][j-1]有关,所以只需在求dp[i-1]时记录dp[i-1][...]的最大值即可,定义该数组为maxj[i]=max(dp[i][0~j]),j是从1到n的

dp[j]代表分为i组时,求到第j个数时的最大值。通过循环求i=0~m,当i==m时,求出dp最大值即为答案。实际上也就是i的最后一次中求出的maxj[n]。

在每一次j的循环中,maxj被需要是在求dp[j+1]的时候,但是maxj[j]是通过dp[1~j]中求出来的,所以加一个临时变量。嗯。我是有多蠢才想了那么久T^T。

好了,啰嗦完了,上代码。

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>

using namespace std;
const int N = 1000005;
const int INF = 0x7fffffff;
int a[N], dp[N], maxj[N];

int main()
{
    int n, m;
    while (scanf("%d%d", &m, &n) != EOF) {
        for (int i = 1; i <= n; i++) {
            scanf("%d", &a[i]);
        }
        memset(dp, 0, sizeof (dp));
        memset(maxj, 0, sizeof (maxj));
        int temp;
        for (int i = 1; i <= m; i++) {
            temp = -INF;
            for (int j = i; j <= n; j++) {
                //注意j>=i所以j从i开始循环
                dp[j] = max(dp[j - 1], maxj[j - 1]) + a[j];
                maxj[j - 1] = temp;
                if (temp < dp[j])
                    temp = dp[j];
            }
        }
        printf("%d
", temp);
    }
    return 0;
}

  

原文地址:https://www.cnblogs.com/wenruo/p/4492849.html