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 S1, S2, S3, S4?... Sx, ... Sn?(1 ≤ x ≤ n ≤ 1,000,000, -32768 ≤ Sx?≤ 32767). We define a function sum(i, j) = Si?+ ... + Sj?(1 ≤ i ≤ j ≤ n).
Now given an integer m (m > 0), your task is to find m pairs of i and j which make sum(i1, j1) + sum(i2, j2) + sum(i3, j3) + ... + sum(im, jm) maximal (ix?≤ iy?≤ jx?or ix?≤ jy?≤ jx?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 sum(ix, jx)(1 ≤ x ≤ m) instead. ^_^
Input
Each test case will begin with two integers m and n, followed by n integers S1, S2, S3?... Sn. Process to the end of file.
Output
Output the maximal summation described above in one line.
Sample
Input | Output |
---|
1 3 1 2 3 2 6 -1 4 -2 3 -2 3 |
6 8 |
Hint Huge input, scanf and dynamic programming is recommended.
题意:?给定n个数以及参数k,求这n个数的最大k子段和。
分析:?这道题题目不是很清楚,没明确说明k的数值范围,而且还是多组数据,可能对解题方法的选取有一定的影响。在知道两重for循环更新也不会超时后这道题就比较简单了,设dp[i][j]表示前i个数的最大j子段和,其中第i个数必须被选取,这样状态转移可以分两种情况,第一种是第i个数并入前一段,第二种是第i个数自己新开一段,这种情况需要用维护前缀max值,因为不知道哪个点能够取到最大。
如果数据比较小的话dp数组开两维就过了,不过这道题需要空间优化,直接用一维的滚动数组还需要维护别的东西,所以直接第二维开成2,用%2的滚动数组。这里有一个点要注意,下图为未空间优化时的dp表,
?接下来是滚动数组优化后的dp表:?
当i取2,j取3、5、7等奇数时,本来用于更新的两个值都是-inf,但滚动数组优化完后dp[1][1]的值还是a[1],所以在更新时就会出问题,只要j为2的那一列更新完后置dp[1][1]为-inf即可。
具体代码如下:
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <string>
#define int long long
#define inf 0x3f3f3f3f3f3f3f3f
using namespace std;
//最大k子段和
//题目数据范围表述不清!!!
//dp[i][j]表示前i个数的最大k段和,必选第i个,下面用了滚动数组
int k, n, dp[1000005][2], a[1000005];
signed main()
{
while(~scanf("%lld%lld", &k, &n)){
memset(dp, -0x3f, sizeof dp);
for(int i = 1; i <= n; i++)
scanf("%lld", &a[i]);
//边界初始化
dp[1][1] = a[1];
for(int i = 2; i <= n; i++){
if(dp[i-1][1] >= 0) dp[i][1] = dp[i-1][1]+a[i];
else dp[i][1] = a[i];
}
//并入上一段还是自己新开一段
for(int j = 2; j <= k; j++){
int _max = -inf;
for(int i = 2; i <= n; i++){
_max = max(dp[i-1][(j-1)%2], _max);
dp[i][j%2] = max(dp[i-1][j%2], _max)+a[i];
}
if(j == 2) dp[1][1] = -inf;
}
int ans = -inf;
for(int i = 1; i <= n; i++)
ans = max(ans, dp[i][k%2]);
printf("%lld\n", ans);
}
return 0;
}
|