环状最大两段子段和
题目描述
给出一段环状序列,即认为A[1]和A[N]是相邻的,选出其中连续不重叠且非空的两段使得这两段和最大。
输入输出格式
输入格式:
输入文件maxsum2.in的第一行是一个正整数N,表示了序列的长度。
第2行包含N个绝对值不大于10000的整数A[i],描述了这段序列,第一个数和第N个数是相邻的。
输出格式:
输入文件maxsum2.out仅包括1个整数,为最大的两段子段和是多少。
输入输出样例
输入样例#1:
7 2 -4 3 -1 2 -4 3
输出样例#1:
9
题解:动态规划
最大两段子段和有两种情况:(x表示子段)
1.--xxxx--xxxx--
2.xxx---xxx---xxx
对于情况1,求出i点右边的最大字段和ti,左边的fi,ans1=max(f[i]+t[i+1])
对于2,等价于求两段最小子段和,求出i点右边的最小字段和ti,左边的fi
ans2=sum-min(f[i]+t[i+1])
有一个细节,因为必须有数,所以当f[i]+t[i+1]==sum时,要特判
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 using namespace std; 6 typedef long long lol; 7 lol sum,a[500001],tmp[500001],f[500001],t[500001],maxans,maxx,minx,minans; 8 int main() 9 {int n,i,j; 10 cin>>n; 11 maxans=-2e18; 12 for (i=1;i<=n;i++) 13 { 14 scanf("%lld",&a[i]); 15 sum+=a[i]; 16 } 17 maxx=-2e18; 18 for (i=1;i<=n;i++) 19 { 20 if (tmp[i-1]<0) tmp[i]=a[i]; 21 else tmp[i]=tmp[i-1]+a[i]; 22 if (maxx<tmp[i]) maxx=tmp[i]; 23 f[i]=maxx; 24 } 25 maxx=-2e18; 26 for (i=n;i>=1;i--) 27 { 28 if (tmp[i+1]<0) tmp[i]=a[i]; 29 else tmp[i]=tmp[i+1]+a[i]; 30 if (maxx<tmp[i]) maxx=tmp[i]; 31 t[i]=maxx; 32 } 33 for (i=1;i<=n-1;i++) 34 maxans=max(maxans,f[i]+t[i+1]); 35 36 minx=2e18; 37 for (i=1;i<=n;i++) 38 { 39 if (tmp[i-1]>0) tmp[i]=a[i]; 40 else tmp[i]=tmp[i-1]+a[i]; 41 if (minx>tmp[i]) minx=tmp[i]; 42 f[i]=minx; 43 } 44 minx=2e18; 45 for (i=n;i>=1;i--) 46 { 47 if (tmp[i+1]>0) tmp[i]=a[i]; 48 else tmp[i]=tmp[i+1]+a[i]; 49 if (minx>tmp[i]) minx=tmp[i]; 50 t[i]=minx; 51 } 52 minans=2e18; 53 for (i=1;i<=n-1;i++) 54 minans=min(minans,f[i]+t[i+1]); 55 if (sum==minans||maxans>sum-minans) 56 cout<<maxans<<endl; 57 else cout<<sum-minans; 58 }