#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子段和
这道题要求输出整数解, 需要特判一下, 如果分段数大于正整数个数, 输出所有正整数的和;
#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;
}