Max Sum Plus Plus
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 40437 Accepted Submission(s): 14558
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. ^_^
Process to the end of file.
6 8
Hint
Huge input, scanf and dynamic programming is recommended.
题目链接:
http://acm.hdu.edu.cn/showproblem.php?pid=1024
题目大意:
一个长度为n的序列,求出最大m个子段的序列元素和
思路:
这题的数据特别大,用暴力显然是不行的,所以得想别的方法。
我们可以先看暴力做法的大致思路:首先,我们应该是遍历每一种将n个数选取m段连续子段然后求出他们的最大和,再从这些数里找出前m段相加就是答案,从这个思路中我们可以得到要求出答案,就要先将n个数中选取m段。
因此,我们可以将问题拆分成若干个子问题,求出子问题的最优解,然后在子问题的最优解中挑选出最优解,即得到答案。
现在,我们将问题拆分。
先从1段开始分析:n个数选取1段的最大的和是多少。我们从第一个数开始选,这时的问题就是选一个数作为一段,显然就是这个数的值,然后我们再看第二个数,这时我们就要考虑这两种情况(1:不选这个数和更大。2:选这个数和更大(这种情况不能仅仅用之前选出的最大值+这个数,看第三个数你就知道了)),我们从这两种情况中得出最大的值就是从两个数中选取1段的最优解,我们再看第三个数,也要考虑要不要选他,我们用一个例子来说明:
假设,我们是1,-2,3这三个数,我们在第一第二步得出的最大和都是1(在第二个数时,我们不选取-2,所以最大值仍是1),当到第三个数时,我们因去遍历前面的每一个与3有关的连续字段得出最大值(显然这个值是只选第三个),而不是用前面2个数求出的值+3了(那样的话值为4,显然是错误的)。这时我们得出了3,他和前面1相比取大就好了。
后面的以此类推,一直到第n个数,我们就把n个数分成1段对于每个数的所有最优解求出来了,接下来我们看分成2段时的求法。
![](https://i-blog.csdnimg.cn/blog_migrate/b4e233ee585c2052a94fdf1479e24916.png)
1 #include<stdio.h> 2 #include<iostream> 3 #include<string.h> 4 using namespace std; 5 6 long long dp[2][1001000],num[1001000]; //滚动数组dp,前缀和num->为了减少当子段数很大时遍历前缀求和所花费时间 7 long long maxx; 8 int main() 9 { 10 long long n,m,k; 11 while(scanf("%lld%lld",&n,&m)!=EOF) 12 { 13 int t=0; 14 memset(dp,0,sizeof(dp)); 15 memset(num,0,sizeof(num)); 16 maxx=0; 17 for(int i=1;i<=m;i++) 18 { 19 scanf("%lld",&k); 20 num[i]=num[i-1]+k; 21 } 22 for(int i=1;i<=n;i++) 23 { 24 for(int j=i;j<=m;j++) 25 { 26 if(i==j) maxx=dp[t][j]=num[j]; //当i==j时 当前最大值为前j项和,即dp数组为num[j] 27 else 28 { 29 maxx=max(maxx,dp[1-t][j-1])+num[j]-num[j-1]; //maxx更新 30 dp[t][j]=max(dp[t][j-1],maxx); 31 } 32 } 33 t=1-t; //数组滚动 34 } 35 t=1-t; //滚动数组最后一次遍历后 与 答案数组相反 进行一次t的改变 36 cout<<dp[t][m]<<endl; 37 } 38 return 0; 39 }
编辑:常远帆 刘作霖 石启隆 夏正然 陈耀庭