洛谷 P1121 环状最大两段子段和 题解
每日一题 day57 打卡
Analysis
对于这个问题,由于分成了两个子序列,我们不妨就是枚举一下可能出现的情况:
无非就这两种:
1.+++++0000+++++0000++++
2.0000++++00000++++000000
0就表示选了这个数,+就表示不选这个数,
那我们正反先做一个普通的最大子序列,就可以完成第2中情况,然后再求个最小子序列,把总和一减就是第1种情况。
至于求最小子序列,可以把数字都负过来,然后再搞个最大子序列就好了。
楼下举的例子非常对,我仔细思考了一下这个特例,即
4 -1 1 -1 -1
当我们将数字负过来时,就成了:
4 1 -1 1 1
此时的两个最大子序列我们会选成:
0+00 而首尾都选了,其实就是只选了一个序列,而这种特殊情况又只会在
只有一个数是正数时出现,所以特判一下就好了,如果只有一个数是正数,我们就不做将数字负过来后的那个最大子序列了,
直接找两个最大的数累和就行了。
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #define int long long 6 #define maxn 200000+10 7 #define INF 2147483647 8 #define rep(i,s,e) for(register int i=s;i<=e;++i) 9 #define dwn(i,s,e) for(register int i=s;i>=e;--i) 10 using namespace std; 11 inline int read() 12 { 13 int x=0,f=1; 14 char c=getchar(); 15 while(c<'0'||c>'9') {if(c=='-') f=-1; c=getchar();} 16 while(c>='0'&&c<='9') {x=x*10+c-'0'; c=getchar();} 17 return f*x; 18 } 19 inline int write(int x) 20 { 21 if(x<0) {putchar('-'); x=-x;} 22 if(x>9) write(x/10); 23 putchar(x%10+'0'); 24 } 25 int n,sum1,sum2; 26 int a[maxn],dp_top[maxn],dp_back[maxn]; 27 inline int calc() 28 { 29 int res=-INF; 30 rep(i,1,n) dp_top[i]=max(dp_top[i-1],0ll)+a[i]; 31 dwn(i,n,1) dp_back[i]=max(dp_back[i+1],0ll)+a[i]; 32 rep(i,1,n) dp_top[i]=max(dp_top[i-1],dp_top[i]); 33 dwn(i,n,1) dp_back[i]=max(dp_back[i+1],dp_back[i]); 34 rep(i,1,n-1) res=max(res,dp_top[i]+dp_back[i+1]); 35 return res; 36 } 37 signed main() 38 { 39 memset(a,128,sizeof(a)); 40 n=read(); 41 rep(i,1,n) 42 { 43 a[i]=read(); 44 sum1+=a[i]; 45 if(a[i]>0) sum2+=a[i]; 46 } 47 if(sum2==0) 48 { 49 int minn1=-INF,num=0; 50 rep(i,1,n) 51 { 52 if(a[i]>minn1) 53 { 54 minn1=a[i]; 55 num=i; 56 } 57 } 58 a[num]=-INF; 59 int minn2=-INF; 60 rep(i,1,n) 61 if(a[i]>minn2) minn2=a[i]; 62 write(minn1+minn2); 63 return 0; 64 } 65 int ans1=calc(); 66 rep(i,1,n) a[i]=-a[i]; 67 int ans2=sum1+calc(); 68 if(ans2==0) ans2=-INF; 69 write(max(ans1,ans2)); 70 return 0; 71 }
请各位大佬斧正(反正我不认识斧正是什么意思)