环形m段最大子段和 题解动态规划DP

http://acm.hunnu.edu.cn/online/?action=problem&type=show&id=10581

 

环形m段最大子段和
Time Limit: 5000ms, Special Time Limit:15000ms, Memory Limit:65536KB
Total submit users: 3, Accepted users: 3
Problem 10581 : No special judgement
Problem description
  我想你已经做出了一维的最大子段和问题,也许你还做过环形的最大子段和问题,也许你还做过m段的最大子段和。所以,我们这里不再多解释题意了。作为一个勇敢而且坚强的ACMer,你现在的问题是求一个环形的m段最大子段和。

 

好吧,我就说详细点吧,题目所求即是,在一个有n个数的环形序列上,找到m个不重叠的非空子序列,使得这些子序列所有数之生最大。

Input
  多组测试数据,以EOF结束。

 

对于每组测试数据有两行。

第一行是两个正整数n,m0<n<=100000,0<m<=100000,0<n*m<=1000000,其中n表示环上有多少个数字,m表示最大子段和的段数。而且有m<=n

接下来一行为n个整数v,依顺时针表示此环上的n个整数。对于每个整数有 -1000<=v<=1000

Output
  对于每组测试数据,输出占一行,即此环形的m段最大子段和。

Sample Input
3 2
1 -1 0
4 2
1 -1 1 -1
5 2
1 -1 1 -1 1
Sample Output
1
2
3
Problem Source
  IGrassMudMahlerGobi

 

 

官方题解:

这道题比网易有道第一场的第3题显然是难一些。其实是两个问题拼在一起。环形最大子段和,m段最大子段和,如果这两个题都做过的话,就是容易题了,否则比较难。

 

此题有错误。

我一直WA,犯了两个错误

1.我记录正数个数,小于m就排序,取前m个最大的 ,正确的是小于等于m就排序

2.结果取min(max_sum(m,n),sum-min_sum(m,n))

题目有错,应该取max(max_sum(m,n),sum-min_sum(m,n))

 

代码:

#include<cstdio> #include<algorithm> using namespace std; const int N=1000010,INF=-1u>>1; int f[N],g[N],a[N],ff[N],gg[N]; int max_sum(int m,int n)//O(nm) { int i,j,t; for(i=1;i<=n;i++) { t=min(i,m); for(j=1;j<=t;j++) { f[j]=max(f[j],g[j-1])+a[i]; g[j-1]=max(g[j-1],f[j-1]); } g[j-1]=max(g[j-1],f[j-1]); } return g[m]; } int min_sum(int m,int n)//O(nm) { int i,j,t; for(i=1;i<=n;i++) { t=min(i,m); for(j=1;j<=t;j++) { ff[j]=min(ff[j],gg[j-1])+a[i]; gg[j-1]=min(gg[j-1],ff[j-1]); } gg[j-1]=min(gg[j-1],ff[j-1]); } return gg[m]; } int main() { int i,m,n,ans,sum; while(scanf("%d%d",&n,&m)==2) { int flag=0; sum=0; ans=-INF; for(i=1;i<=n;i++) { f[i]=g[i]=-INF; ff[i]=gg[i]=INF; scanf("%d",a+i); sum+=a[i]; if(a[i]>0) flag++; } if(flag<=m) { sort(a+1,a+n+1); for(ans=i=0;i<m;i++) ans+=a[n-i]; } else ans=max(max_sum(m,n),sum-min_sum(m,n)); printf("%d/n",ans); } return 0; }

你可能感兴趣的:(环形m段最大子段和 题解动态规划DP)