最大子段和(四种方法)以及进阶的最大m子段和+两道例题

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int maxn = 1e6+100;
const int INF = 0x3f3f3f3f;
int a[1000], n, m;
/*
方法一; 
	枚举每个子段和;
	{a0},{a0, a1},{a0, a1, ..., an-1};
	{a1},{a1, a2},{a1, a2, ..., an-1};
	...;
	{an-1}; 
	时间复杂度:O(n^3); 
*/
int solve_1(){
	int ans=0, sum=0;
	for(int i=1; i<=n; i++){
		for(int j=i; j<=n; j++){
			sum=0;
			for(int k=i; k<=j; k++){
				sum+=a[k];
			}
			ans=max(ans, sum);
		}
	}
	return ans;
}
/*
方法二; 
	和一的思想一样, 优化一下;
	时间复杂度:O(n^2);	
*/
int solve_2(){
	int ans=0, sum=0;
	for(int i=1; i<=n; i++){
		sum=0;
		for(int j=i; j<=n; j++){
			sum+=a[j];
			ans=max(ans, sum);
		}
	}
	return ans;
}
/*
方法三:
	分治;
	最大子段有可能在左侧, 也可能在右侧, 也可能是左右合并; 
	时间复杂度:O(nlogn); 
*/	
int solve_3(int left, int right){
	int leftmax, rightmax;  
	if(left==right) return a[left]>0?a[left]:0;
	int mid=(left+right)/2;
	leftmax=solve_3(left, mid);
	rightmax=solve_3(mid+1, right);
	int sum, leftsum, rightsum;
	sum=leftsum=0;
	for(int i=mid; i>=left; i--){
		sum+=a[i];
		if(sum>leftsum) leftsum=sum;
	}
	sum=rightsum=0;
	for(int i=mid+1; i<=right; i++){
		sum+=a[i];
		if(sum>rightsum) rightsum=sum;
	}
	int retsum=leftsum+rightsum;
	return max(retsum, max(leftmax, rightmax));
}
/*
方法四:
	动规;
	只要前边和不为负数, 那么加上后边一个正数, 结果有可能会变大; 
	时间复杂度:O(n); 
*/
int dp[1000];
int solve_4(){
	dp[0]=0;
	int ans=dp[0];
	for(int i=1; i<=n; i++){
		if(dp[i-1]+a[i]<=0) dp[i]=a[i];
		else dp[i]=dp[i-1]+a[i];
		ans=max(dp[i], ans);
	}
	return ans;
} 
/*
将最大子段和问题升级为最大m子段和;
即将数列分为m段不相交子段, 求最大和; 
*/ 
/*
一: 定义二维数组dp[i][j]表示前j个数分成i组的最大和;
dp[i][j]=max(dp[i][j-1](第j个和前边的一起划分到第i组), max(dp[i-1][i-1~j-1](j单独作为第i组)))+a[j];
时间复杂度: O(mn^2) 
*/
int DP[1000][1000]; 
int solve_m_1(){
	int ans=-INF;
	memset(DP, 0, sizeof(DP));
	for(int i=1; i<=m; i++){
		DP[i][i]=DP[i-1][i-1]+a[i];
		for(int j=i+1; j<n; j++){
			int temp=-INF;
			for(int k=i-1; k<=j-1; k++){
				temp=max(temp, DP[i-1][k]);
			}
			DP[i][j]=max(DP[i][j-1], temp)+a[j];
		}
	}
	for(int i=m; i<=n; i++)
		ans=max(ans, DP[m][i]);
	return ans;
}
/*
上个方法时间复杂度有点大, 能不能再降低呢?
可以!max(dp[i-1][i-1~j-1])的计算方法其实是可以省去一层循环的;
而且在空间复杂度上也可以降到一维; 
*/
int solve_m_2(){
	int ans=-INF;
	int temp[1000];
	memset(temp, 0, sizeof(temp));
	memset(dp, 0, sizeof(dp));
	for(int i=1; i<=m; i++){
		ans=-INF;
		for(int j=i; j<=n; j++){
			dp[j]=max(dp[j-1], temp[j-1])+a[j];
			temp[j-1]=ans;
			ans=max(ans, dp[j]);
		}
	}
	return ans;
}
int main(){
	scanf("%d%d", &n, &m);
	for(int i=1; i<=n; i++){
		scanf("%d", &a[i]);
	}
	printf("1:%d\n2:%d\n3:%d\n4:%d\nm_1:%d\nm_2:%d\n", solve_1(), solve_2(), solve_3(1, n), solve_4(), solve_m_1(), solve_m_2());
	return 0;
}

下面附上两道练习题:

HDU 1024  Max Sum Plus Plus

这道题用一维数组做, 二维空间复杂度太大, 数组开不开;

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int maxn = 1e6+100;
const int INF = 0x3f3f3f3f;
int dp[maxn], s[maxn], temp[maxn]; 
int main(){
	int n, m;
	while(~scanf("%d%d", &m, &n)){
		for(int i=1; i<=n; i++){
			scanf("%d", &s[i]);
		}
		memset(dp, 0, sizeof(dp));
		memset(temp, 0, sizeof(temp));
		int ans=-INF;
		for(int i=1; i<=m; i++){
			ans=-INF;
			for(int j=i; j<=n; j++){
				dp[j]=max(dp[j-1]+s[j], temp[j-1]+s[j]);
				temp[j-1]=ans;
				ans=max(ans, dp[j]);
			}
		}
		printf("%d\n", ans);
	}
	return 0;
} 

最大M子段和

51Nod - 1052

这道题要求输出整数解, 需要特判一下, 如果分段数大于正整数个数, 输出所有正整数的和;

#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
const long long INF = 0x3f3f3f3f;
long long a[5100];
long long dp[5100], temp[5100];
int main(){
	int N, M, cnt=0;
	scanf("%d%d", &N, &M);
	for(int i=1; i<=N; i++){
		scanf("%lld", &a[i]);
		if(a[i]>0) cnt++;
	} 
	if(cnt<=M){//特判;
		long long ans=0;
		for(int i=1; i<=N; i++)
			ans+=(a[i]>=0?a[i]:0);
		printf("%lld\n", ans);
		return 0;
	}
	memset(dp, 0, sizeof(dp)); 
	memset(temp, 0, sizeof(dp));
	long long ans;
	for(int i=1; i<=M; i++){
		ans=-INF;
		for(int j=i; j<=N; j++){
			dp[j]=max(dp[j-1], temp[j-1])+a[j];
			temp[j-1]=ans;
			ans=max(ans, dp[j]);
		}
	}
	printf("%lld\n", ans);
	return 0;
}




猜你喜欢

转载自blog.csdn.net/sirius_han/article/details/80313559