这题理解起来有点烦,如果用O(n^2)也可以做,当然是有O(n)的算法的,利用类似于贪心算法,从前往后遍历,每次都加上当前元素,如果小于0,则说明之前所有元素(包括当前元素)的最大子序列和小于0,我们就把当前和置为0,然后判断最大值是否小于当前和。
我也说不清。。。下面摘抄别人的解释:
重点的一个思想是:如果a[i]是负数那么它不可能代表最有序列的起点,因为任何包含a[i]的作为起点的子序列都可以通过用a[i+1]作为起点来改进。类似的有,任何的负的子序列不可能是最优子序列的前缀。例如说,循环中我们检测到从a[i]到a[j]的子序列是负数,那么我们就可以推进i。关键的结论是我们不仅可以把i推进到i+1,而且我们实际可以把它一直推进到j+1。
举例来说,令p是i+1到j之间的任何一个下标,由于前面假设了a[i]+…+a[j]是负数,则开始于下标p的任意子序列都不会大于在下标i并且包含从a[i]到a[p-1]的子序列对应的子序列(j是使得从下标i开始成为负数的第一个下标)。因此,把i推进到j+1是安全的,不会错过最优解。注意的是:虽然,如果有以a[j]结尾的某序列和是负数就表明了这个序列中的任何一个数不可能是与a[j]后面的数形成的最大子序列的开头,但是并不表明a[j]前面的某个序列就不是最大序列,也就是说不能确定最大子序列在a[j]前还是a[j]后,即最大子序列位置不能求出。但是能确保maxSum的值是当前最大的子序列和。这个算法还有一个有点就是,它只对数据进行一次扫描,一旦a[j]被读入处理就不需要再记忆。它是一个联机算法。
这题还有几个注意的地方:
1. 如果全是负数
2. 如果数组中除了0之外就是负数。
下面上代码:
#include <iostream> #include <fstream> using namespace std; bool isAllNeg(int a[], int n) { int i = 0; for(i=0; i<n; i++) if(a[i] >= 0) return false; return true; } int main() { //fstream cin("a.txt"); int num; cin>>num; int *A = new int[num]; int i = 0; while (i < num) { cin>>A[i]; i++; } int Max, Cur; Max = Cur = 0; int start, end, curStart; start = end = curStart = A[0]; for(int i = 0; i < num; i++) { Cur += A[i]; if(Cur < 0) { Cur = 0; (i + 1) < num ? curStart = A[i + 1]:curStart = curStart; } if(Max < Cur) { Max = Cur; end = A[i]; start = curStart; } } if(isAllNeg(A, num)) cout<<0<<" "<<A[0]<<" "<<A[num - 1]; else if(Max == 0) cout<<0<<" "<<0<<" "<<0; else cout<<Max<<" "<<start<<" "<<end<<endl; }