Max Sum Plus Plus
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)Total Submission(s): 15733 Accepted Submission(s): 5116
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). We define a function sum(i, j) = S i + ... + S j (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(i 1, j 1) + sum(i 2, j 2) + sum(i 3, j 3) + ... + sum(i m, j m) maximal (i x ≤ i y ≤ j x or i x ≤ j y ≤ j x 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(i x, j x)(1 ≤ x ≤ m) instead. ^_^
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). We define a function sum(i, j) = S i + ... + S j (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(i 1, j 1) + sum(i 2, j 2) + sum(i 3, j 3) + ... + sum(i m, j m) maximal (i x ≤ i y ≤ j x or i x ≤ j y ≤ j x 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(i x, j x)(1 ≤ x ≤ m) instead. ^_^
Input
Each test case will begin with two integers m and n, followed by n integers S
1, S
2, S
3 ... S
n.
Process to the end of file.
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 8HintHuge input, scanf and dynamic programming is recommended.
①当m=1时,
则该问题变为求最大字段和的问题
②当m>1时
设b(i,j)表示前j个元素(必定包含第j个元素)分为互不相交的i段所得的最大i子段和并且i<=j。 (注:b(i,j)不一定是最优最大i子段和)
因此在考虑第j个元素时,可能存在两种情况:
1)第j个元素和前一个元素一起包含在第i段中;
2)第j个元素独自划分在第i段中。
根据问题的分析,两种情况分别如下:
1)b(i,j-1)表示第j-1个元素的最大j子段和,所以b(i,j)=b(i,j-1)+a[j].
2)max{b(i-1,k)}其中k=i-1..j-1.即表示为在第j个元素之前得到的i-1个子段之和的最优选择。所以b(i,j)=max{b(i-1,k)+a[j]},其中k=i-1..j-1.
综上:b(i,j)=max{b(i,j-1)+a[j],max b(i-1,k)+a[j]},其中k=i-1..j-1.
优化后代码:
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
#define INF 99999999
using namespace std;
const int MAX=1000001;
int *s,*present,*prior;
//present代表前j个数且包括第j个数的i段子序列最大和,prior表示前j个数i段子序列的最大和
int main()
{
int m,n;
while(cin>>m>>n)
{
s=new int[n+1];
for(int i=1;i<=n;++i)
cin>>*(s+i);
//scanf("%d",s+i);
present=new int[n+1];
prior=(int *)calloc(n+1,sizeof(int));//calloc和maccoc区别在于它分配的内存连续且已对数据初始化为0
present[0]=0;//只需要对present[0]初始化,因为present[n]可以由present[n-1]得到
int sum=0;
for(int i=1;i<=m;++i)
{
sum=-INF;
for(int j=i;j<=n-m+i;++j)
{
//present[j-1]在这里代表前j-1个数i段序列的最大值,当j=j-1时就已经计算了present[j-1]
//prior[j-1]在这里代表前j-1个数i-1段序列的最大值,因为还没更新prior[j-1]为i段的最大值
present[j]=max(present[j-1]+s[j],prior[j-1]+s[j]);//将第j个数连载前i段的最后面或者自成一段与前i-1段合成i段
prior[j-1]=sum;//更新为前j-1个数i段的最大值,sum就是前j-1个数i段的最大值
sum=max(present[j],sum);//计算前j个数i段的最大值
}//present[j]=max(present[j-1]+s[j],prior[j-1]+s[j]);prior[j-1]=(present[j-1],prior[j-2]);
prior[n-m+i]=sum;//prior[n]=max(present[n],prior[n-1]);
}
cout<<sum<<endl;
delete[] s;
delete[] present;
delete[] prior;
}
return 0;
}
未优化代码:
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
#define N 1000
int b[N][N];
int MaxSubsum(int m,int n,int a[])
{
if(n < m || m<1)
return 0;
for(int i = 0;i <= m;i++)
b[i][0] = 0;
for(int j = 1;j <= n;j++)
b[0][j] = 0;
for(int i = 1;i <= m;i++)
{
for(int j = i;j <= n-m+i;j++)
{ //n-m+i确保后面的元素可以够分成 m-i 段
if(j>i)
{
b[i][j] = b[i][j-1] + a[j-1];
for(int k = i-1;k < j;k++)
{
if(b[i][j] < b[i-1][k]+a[j-1])
b[i][j] = b[i-1][k] + a[j-1];
}
}
else
b[i][j] = b[i-1][j-1] + a[j-1];
}
}
int sum = 0;
for(int j = m;j <= n;j++)
{
if(sum < b[m][j])
sum = b[m][j];
}
return sum;
}
int main()
{
int m,n;
while(cin>>m>>n)
{
int i,j;
int a[N];
for(i=0;i<n;++i)
cin>>a[i];
cout<<MaxSubsum(m,n,a)<<endl;
}
}