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\n", temp); } return 0; }