天天看点

WEEK12 作业 C - 必做题3C - 必做题3

C - 必做题3

题目描述

东东每个学期都会去寝室接受扫楼的任务,并清点每个寝室的人数。

每个寝室里面有ai个人(1<=i<=n)。从第i到第j个宿舍一共有sum(i,j)=a[i]+…+a[j]个人

这让宿管阿姨非常开心,并且让东东扫楼m次,每一次数第i到第j个宿舍sum(i,j)

问题是要找到sum(i1, j1) + … + sum(im,jm)的最大值。且ix <= iy <=jx和ix <= jy <=jx的情况是不被允许的。也就是说m段都不能相交。

注:1 ≤ i ≤ n ≤ 1e6 , -32768 ≤ ai ≤ 32767 人数可以为负数。。。。(1<=n<=1000000)

Input

输入m,输入n。后面跟着输入n个ai 处理到 EOF

Output

输出最大和。

Sample Input

1 3 1 2 3
2 6 -1 4 -2 3 -2 3
           

Sample Output

6
8
           

Hint

数据量很大,需要scanf读入和dp处理。

题解

区间动态规划问题。

dp[i][j]:在前j个元素中选出不重合的i段的总和是多少(且第j个元素a[j]必须包含在其中)。

状态转移方程:dp[i][j]=max{dp[i][j-1]+a[j],dp[i-1][k]+a[j]} i-1<=k<=j-1将a[j]放在上一段末尾或将a[j]放在新的一段。

但是由于数据量很大,不可能同时存储这么多数,且我们注意到,dp[i-1][k]只在dp[i][j]的更新中用到,所以可以记录下在a[1]-a[j]中选i-1(上一次循环)段的最大和结果数,在时间和空间上同时优化掉一维。令mx[j]为上一个状态的dp最大值,即在a[1]-a[j]中选i-1段的最大和。

优化后的状态转移方程:dp[j]=max(dp[j-1]+a[j],mx[j-1]+a[j])。

最后结果就是选i段的最大值ans。

代码

#include <iostream>
#include <stdio.h>
#include <string.h>
#include <algorithm>
using namespace std;
int a[1000100],dp[1000100],mx[1000100];
int main(int argc, char** argv) {
	int m,n;
	while(scanf("%d%d",&m,&n)!=EOF){
		memset(dp,0,sizeof(dp));
		memset(mx,0,sizeof(mx));
		for(int i=1;i<=n;i++){scanf("%d",&a[i]);}
		int ans;
		for(int i=1;i<=m;i++){
			ans=-100000000;
			for(int j=i;j<=n;j++){
				dp[j]=max(dp[j-1]+a[j],mx[j-1]+a[j]);
				mx[j-1]=ans;
				ans=max(ans,dp[j]);
			}
		}
		printf("%d\n",ans);
	}
	return 0;
}