hdu1024 最大M段子段和

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. ^_^
 

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.
 

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 8
Hint
Huge 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;
	}
}




  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值