01-复杂度1 最大子列和问题
给定KK个整数组成的序列{ N_1N1, N_2N2, ..., N_KNK },“连续子列”被定义为{ N_iNi, N_{i+1}Ni+1, ..., N_jNj },其中 1 \le i \le j \le K1≤i≤j≤K。“最大子列和”则被定义为所有连续子列元素的和中最大者。例如给定序列{ -2, 11, -4, 13, -5, -2 },其连续子列{ 11, -4, 13 }有最大的和20。现要求你编写程序,计算给定整数序列的最大子列和。
本题旨在测试各种不同的算法在各种数据情况下的表现。各组测试数据特点如下:
- 数据1:与样例等价,测试基本正确性;
- 数据2:102个随机整数;
- 数据3:103个随机整数;
- 数据4:104个随机整数;
- 数据5:105个随机整数;
输入格式:
输入第1行给出正整数KK (\le 100000≤100000);第2行给出KK个整数,其间以空格分隔。
输出格式:
在一行中输出最大子列和。如果序列中所有整数皆为负数,则输出0。
输入样例:
6
-2 11 -4 13 -5 -2
输出样例:
20
---------------------------------------------
这里给出四个算法
算法1:空间复杂度 0(n^3)
1 int maxSubList1(int a[], int n) 2 { 3 int max = 0; 4 int i,j,k; 5 int thisSum; 6 for (i = 0; i < n; ++i) 7 { 8 for(j = i; j < n; ++j) 9 { 10 thisSum = 0; 11 for(k = i; k < j; k++) 12 thisSum += a[k]; 13 if(thisSum > max) 14 max = thisSum; 15 } 16 } 17 return max; 18 }
算法2:复杂度0(n^2)
int maxSubList2(int a[], int n) { int max = 0; int i,j; int thisSum; for (i = 0; i < n; ++i) { thisSum = 0; for(j = i; j < n; ++j) { thisSum += a[j]; if(max < thisSum) max = thisSum; } } return max; }
算法3 ,分而治之 ,复杂度O(Nlogn)
说实话,这个算法,怎么明白,听陈约姥姥讲的,听明白了算法,不知道怎么实现
// 求ab的最大值 int max(int a, int b) { return a > b ? a : b; } // 第三个算法 int maxSubList3(int a[], int left, int right) { // 如果只有一个数 int i; int center = (left + right) / 2; //中点 int leftMaxSum, rightMaxSum, lrMaxSum; int maxLeftSum = 0; int leftSum = 0; //跨越边界的左端值最大 int maxRightSum = 0; int rightSum = 0; if(left == right) return a[left]; leftMaxSum = maxSubList3(a, left, center);// 整个出现在输入数据的左半部的最大子序列求和 rightMaxSum = maxSubList3(a, center+1, right);// 整个出现在输入数据的右半部的最大子序列求和 lrMaxSum = max(leftMaxSum, rightMaxSum); //计算左右两个子序列求和结果的最大值 // 横跨左右两个部分的最大子序列求和 //从center向左处理左半边 for (i = center; i >= left; i--) { leftSum += a[i]; maxLeftSum = max(maxLeftSum, leftSum); } for (i = center+1; i <= right; ++i) { rightSum =+ a[i]; maxRightSum = max(maxRightSum, rightSum); } return max(lrMaxSum, maxLeftSum + maxRightSum); }
算法4,在线测试,所谓在线就是一次便利,随搞随用 复杂度O(N)
这个算法还是比较好理解的
int maxSubList4(int A[], int N ) { int ThisSum, MaxSum; int i; ThisSum = MaxSum = 0; for( i = 0; i < N; i++ ) { ThisSum += A[i]; /* 向右累加 */ if( ThisSum > MaxSum ) MaxSum = ThisSum; /* 发现更大和则更新当前结果 */ else if( ThisSum < 0 ) /* 如果当前子列和为负 */ ThisSum = 0; /* 则不可能使后面的部分和增大,抛弃之 */ } return MaxSum; }
最后提交的程序
#include "stdio.h" #define size 100000 int maxSubList1(int a[], int n) { int max = 0; int i,j,k; int thisSum; for (i = 0; i < n; ++i) { for(j = i; j < n; ++j) { thisSum = 0; for(k = i; k < j; k++) thisSum += a[k]; if(thisSum > max) max = thisSum; } } return max; } int maxSubList2(int a[], int n) { int max = 0; int i,j; int thisSum; for (i = 0; i < n; ++i) { thisSum = 0; for(j = i; j < n; ++j) { thisSum += a[j]; if(max < thisSum) max = thisSum; } } return max; } // 求ab的最大值 int max(int a, int b) { return a > b ? a : b; } // 第三个算法 int maxSubList3(int a[], int left, int right) { // 如果只有一个数 int i; int center = (left + right) / 2; //中点 int leftMaxSum, rightMaxSum, lrMaxSum; int maxLeftSum = 0; int leftSum = 0; //跨越边界的左端值最大 int maxRightSum = 0; int rightSum = 0; if(left == right) return a[left]; leftMaxSum = maxSubList3(a, left, center);// 整个出现在输入数据的左半部的最大子序列求和 rightMaxSum = maxSubList3(a, center+1, right);// 整个出现在输入数据的右半部的最大子序列求和 lrMaxSum = max(leftMaxSum, rightMaxSum); //计算左右两个子序列求和结果的最大值 // 横跨左右两个部分的最大子序列求和 //从center向左处理左半边 for (i = center; i >= left; i--) { leftSum += a[i]; maxLeftSum = max(maxLeftSum, leftSum); } for (i = center+1; i <= right; ++i) { rightSum =+ a[i]; maxRightSum = max(maxRightSum, rightSum); } return max(lrMaxSum, maxLeftSum + maxRightSum); } int maxSubList4(int A[], int N ) { int ThisSum, MaxSum; int i; ThisSum = MaxSum = 0; for( i = 0; i < N; i++ ) { ThisSum += A[i]; /* 向右累加 */ if( ThisSum > MaxSum ) MaxSum = ThisSum; /* 发现更大和则更新当前结果 */ else if( ThisSum < 0 ) /* 如果当前子列和为负 */ ThisSum = 0; /* 则不可能使后面的部分和增大,抛弃之 */ } return MaxSum; } int main(int argc, char const *argv[]) { int i, n, a[size]; while(scanf("%d", &n) != EOF) { for (i = 0; i < n; ++i) { scanf("%d", &a[i]); } printf("%d\n", maxSubList4(a, n)); } return 0; }