动态规划[入门]2-循环数组最大子段和
分析:
(1)笨方法,我们可以用普通最大子段和的方法解决这个问题。我们从每个位置“断开”环,然后按普通的最大子段和的方法去做。这样做的复杂度是O(n^2)。
(2)巧妙点的方法,我们之所以要从某个位置切开是因为循环的最大子段和可能是跨越一部分头和尾。
如上图,最优解可能是0..i, j + 1.. n – 1两段,那这时,其实中间i + 1..j是个“最小子段和”,因为总和是一定得嘛。
所以“循环数组得最大子段和”问题,可以把环从任意位置断开,然后求出最优解 = max(普通的最大子段和, 总和 – 普通的“最小子段和”)
求最小子段和,显然也可以用最大子段和的方法求一次就可以了。所以循环数组的最大子段和,实际上是求了两次最大子段和而已。
最后,我们来提供输入输出数据,由你来写一段程序,实现这个算法,只有写出了正确的程序,才能继续后面的课程。
输出示例
输入
第1行:整数序列的长度N(2 <= N <= 50000) 第2 - N+1行:N个整数 (-10^9 <= S[i] <= 10^9)
输出
输出循环数组的最大子段和。
输入示例
6 -2 11 -4 13 -5 -2
输出示例
20
请选取你熟悉的语言,并在下面的代码框中完成你的程序,注意数据范围,最终结果会造成Int32溢出,这样会输出错误的答案。
不同语言如何处理输入输出,请查看下面的语言说明。
1 #include<cstdio> 2 long long max(long long a,long long b){ 3 if (a>b) return a; 4 else return b; 5 } 6 long long min(long long a,long long b){ 7 if (a<b) return a; 8 else return b; 9 } 10 int main(){ 11 int n; 12 long long last,ans,tot,min_ans; 13 int s[50000]; 14 scanf("%d",&n); 15 tot=0; 16 for (int i=0;i<n;i++){ 17 scanf("%d",&s[i]); 18 tot+=s[i]; 19 } 20 last=-100000000LL; 21 ans=-100000000LL; 22 for(int i=0;i<n;i++){ 23 last=max(last,0)+s[i]; 24 ans=max(last,ans); 25 } 26 last=100000000LL; 27 min_ans=100000000LL; 28 for(int i=0;i<n;i++){ 29 last=min(last,0)+s[i]; 30 min_ans=min(last,min_ans); 31 } 32 33 printf("%d",max(ans,tot-min_ans)); 34 35 }
Int32溢出,悲剧!!c++不熟啊!!
上python:
1 n=int(input()) 2 s=[] 3 last=-10000000000 4 ans=-10000000000 5 tot=0 6 min_last=10000000000 7 min_ans=10000000000 8 for i in range(n): 9 s.append(int(input())) 10 tot+=s[i] 11 last=max(last,0)+s[i] 12 ans=max(last,ans) 13 min_last=min(min_last,0)+s[i] 14 min_ans=min(min_ans,min_last) 15 print(max(ans,tot-min_ans)) 16 17 18