#include <iostream> #include <vector> using namespace std; //s(tart)表示最大子序列的开始位置,e(nd)表示结束位置 //这里如果有多于一个的最大子序列的时候,只记录开始位置最低的那个 int s=0; int e=0; //穷举法,复杂度O(n^3) long maxSubSum1(const vector<int> &a){ long maxSum=0; for (int i=0; i<a.size();i++) { for (int j=i;j<a.size();j++) { long thisSum=0; for (int k=i; k<=j; k++) { thisSum+=a[k]; } if (thisSum>maxSum){ maxSum=thisSum; s=i; e=j; } } } return maxSum; } //也是穷举法,不过减去了上面的一些不必要操作O(n^2) long maxSubSum2(const vector<int> &a){ long maxSum=0; for (int i=0; i<a.size(); i++) { long thisSum=0; for (int j=i; j<a.size(); j++) { thisSum+=a[j]; if (thisSum>maxSum){ maxSum=thisSum; s=i; e=j; } } } return maxSum; } long max3(long a, long b, long c){ if(a<b) a=b; if(a>c) return a; else return c; } long maxSumRec(const vector<int> a, int left, int right){ if(left == right) { //其实这个基准值在后面计算的时候可以保证 //在这里不必多此一举 if(a[left]>0) return a[left]; else return 0; } int center=(left+right)/2; long maxLeftSum=maxSumRec(a,left,center); long maxRightSum=maxSumRec(a,center+1,right); //某段序列中,求含最右侧元素序列和的最大值 long maxLeftBorderSum=0,leftBorderSum=0; for (int i=center; i>=left; i--) { leftBorderSum+=a[i]; if(leftBorderSum>maxLeftBorderSum) { maxLeftBorderSum=leftBorderSum; s=i; } } //某段序列中,求含最左侧元素序列和的最大值 long maxRightBorderSum=0,rightBorderSum=0; for (int j=center+1; j<=right; j++) { rightBorderSum+=a[j]; if(rightBorderSum>maxRightBorderSum) { maxRightBorderSum=rightBorderSum; e=j; } } return max3(maxLeftSum,maxRightSum, maxLeftBorderSum+maxRightBorderSum); } //该方法我们采用“分治策略”(divide-and-conquer),相对复杂的O(NlogN)的解法 //最大子序列可能在三个地方出现,或者在左半部,或者在右半部, //或者跨越输入数据的中部而占据左右两部分。前两种情况递归求解, //第三种情况的最大和可以通过求出前半部分最大和(包含前半部分最后一个元素) //以及后半部分最大和(包含后半部分的第一个元素)相加而得到。 long maxSubSum3(const vector<int> &a){ return maxSumRec(a,0,a.size()-1); } //如果a[i]是负数那么它不可能代表最有序列的起点,因为任何包含a[i]的作为起点的子 //序列都可以通过用a[i+1]作为起点来改进。类似的有,任何的负的子序列不可能是最优 //子序列的前缀。例如说,循环中我们检测到从a[i]到a[j]的子序列是负数,那么我们就可以推进i。 //关键的结论是我们不仅可以把i推进到i+1,而且我们实际可以把它一直推进到j+1。 long maxSubSum4(const vector<int> &a){ long maxSum=0; long thisSum=0; int t=0; for (int j=0; j<a.size(); j++) { thisSum+=a[j]; if(thisSum>maxSum){ maxSum=thisSum; s=t; e=j; } else if(thisSum<0){ thisSum=0; t=j+1; } } return maxSum; }
测试程序:
#include <iostream> #include <vector> #include <string> #include <fstream> #include <ctime> #include <cstdlib> using namespace std; const long COUNT = 1000; const int MAX_NUM = 200; int Start = 0; int End = 0; bool readFile(vector<int>&input, string fileName) { ifstream infile(fileName.c_str()); if(!infile) { return false; } int s; while(infile >> s) { input.push_back(s); } return true; } bool writeTestData(string fileName) { ofstream outfile(fileName.c_str()); if(!outfile) { return false; } srand((unsigned)time(NULL)); for(int i = 0; i < COUNT; i++) { if(rand() % 2 == 0) { outfile << rand() % MAX_NUM << endl; } else { outfile << ~(rand() % MAX_NUM) <<endl; } } return true; } long maxSubSum1(const vector<int>& a) { long maxSum = 0; for(int i = 0; i < a.size(); i++) { for(int j = i; j < a.size();j++) { long thisSum = 0; for(int k = i; k <= j; k++) { thisSum += a[k]; } if(thisSum > maxSum) { maxSum = thisSum; Start = i + 1; End = j + 1; } } } return maxSum; } long maxSubSum2(const vector<int>& a) { long maxSum = 0; for(int i = 0; i < a.size(); i++) { long thisSum = 0; for(int j = i; j <a.size(); j++) { thisSum += a[j]; if(thisSum > maxSum) { maxSum = thisSum; Start = i + 1; End = j + 1; } } } return maxSum; } long max3(long a, long b, long c) { if(a < b) { a = b; } if(a > c) { return a; } else { return c; } } long maxSumRec(const vector<int>&a, int left, int right) { if(left == right) { if(a[left] > 0) { return a[left]; } else return 0; } int center = (left + right) / 2; long maxLeftSum = maxSumRec(a, left, center); long maxRightSum = maxSumRec(a, center + 1, right); long maxLeftBorderSum = 0, leftBorderSum = 0; for (int i = center; i >= left; i--) { leftBorderSum += a[i]; if (leftBorderSum > maxLeftBorderSum) { maxLeftBorderSum = leftBorderSum; Start = i + 1; } } long maxRightBorderSum = 0, rightBorderSum = 0; for(int j = center + 1; j <= right; j++) { rightBorderSum += a[j]; if(rightBorderSum > maxRightBorderSum) { maxRightBorderSum = rightBorderSum; End = j + 1; } } return max3(maxLeftSum, maxRightSum, maxLeftBorderSum + maxRightBorderSum); } long maxSubSum3(const vector<int>&a) { return maxSumRec(a, 0, a.size() - 1); } long maxSubSum4(const vector<int>& a) { long maxSum = 0, thisSum = 0; for (int j = 0; j < a.size(); j++) { thisSum += a[j]; if (thisSum > maxSum) maxSum = thisSum; else if (thisSum < 0) thisSum = 0; } return maxSum; } int main() { clock_t start,finish; vector<int>num; if(!writeTestData("in.txt")) { cout << "写入文件错误" << endl; } if(readFile(num, "in.txt")) { start = clock(); cout << maxSubSum1(num) << endl; finish = clock(); //cout << "Start position = " << Start << "\t" << "Start position = " << End <<endl; printf("Time used = %.2lf\n",(double)(finish - start)/CLOCKS_PER_SEC); start = clock(); cout << maxSubSum2(num) << endl; finish = clock(); //cout << "Start position = " << Start << "\t" << "Start position = " << End <<endl; printf("Time used = %.2lf\n",(double)(finish - start)/CLOCKS_PER_SEC); start = clock(); cout << maxSubSum3(num) << endl; finish = clock(); printf("Time used = %.2lf\n",(double)(finish - start)/CLOCKS_PER_SEC); start = clock(); cout << maxSubSum4(num) << endl; finish = clock(); printf("Time used = %.2lf\n",(double)(finish - start)/CLOCKS_PER_SEC); } return 0; }
count = 1000
4135
Time used = 12.87
4135
Time used = 0.06
4135
Time used = 0.00
4135
Time used = 0.00
请按任意键继续. . .
count = 2000
8968
Time used = 104.37
8968
Time used = 0.23
8968
Time used = 0.00
8968
Time used = 0.00
请按任意键继续. . .
count = 10000 //O(N^3)略去
15302
Time used = 5.88
15302
Time used = 0.02
15302
Time used = 0.00
请按任意键继续. . .
count = 100000
33853
Time used = 570.03
33853
Time used = 0.16
33853
Time used = 0.01
请按任意键继续. . .
!!!