标签:mat 一个 ott 整数 baseline 中间 == 解法 sub
给定K个整数组成的序列{ N?1??, N?2??, ..., N?K?? },“连续子列”被定义为{ N?i??, N?i+1??, ..., N?j?? },其中1 ≤ i ≤ j ≤ K。“最大子列和”则被定义为所有连续子列元素的和中最大者。例如给定序列{ -2, 11, -4, 13, -5, -2 },其连续子列{ 11, -4, 13 }有最大的和20。现要求你编写程序,计算给定整数序列的最大子列和。
本题旨在测试各种不同的算法在各种数据情况下的表现。各组测试数据特点如下:
输入第1行给出正整数K (≤ 100000);第2行给出K个整数,其间以空格分隔。
在一行中输出最大子列和。如果序列中所有整数皆为负数,则输出0。
6 -2 11 -4 13 -5 -2
20
第一种是暴力解法,按序列顺序每次选一项,然后从这项开始把后面的每一项相加,如果发现某个子项的和比之前记录的最大和要大,就更新最大和。
AC代码:
1 #include <cstdio> 2 3 void maxSubSeqSum(int *a, int n); 4 5 int main() { 6 int n; 7 scanf("%d", &n); 8 int a[n]; 9 for (int i = 0; i < n; i++) { 10 scanf("%d", a + i); 11 } 12 maxSubSeqSum(a, n); 13 14 return 0; 15 } 16 17 void maxSubSeqSum(int *a, int n) { 18 int maxSum = 0; 19 for (int i = 0; i < n; i++) { 20 int ret = 0; 21 for (int j = i; j < n; j++) { 22 ret += a[j]; 23 if (ret > maxSum) maxSum = ret; 24 } 25 } 26 27 printf("%d", maxSum); 28 }
第二种是分治解法。主要思想是,先求序列左半边的最大子列和,得到leftMaxSum。然后再求序列右半边的最大子列和,得到rightMaxSum。然后求中间部分的最大子列和,方法就是从序列中间项开始,分别求左右两边方向的部分最大子列和,得到左边的leftMax和右边的rightMax,他们的和,也就是leftMax + rightMax,就是中间部分的最大子列和。然后从leftMaxSum,rightMaxSum,leftMax + rightMax三者中选最大的那个,就是整个序列的最大子列和了。
而上述所说的求序列左半边的最大子列和,以及求序列右半边的最大子列和用到的是同样的方法,所以其实就是递归调用。这就是求最大子列和的分治法。
当时我第一次接触到分治法也不是很懂,觉得递归理解起来很难。但学了几个月的算法后,现在理解起来没有什么困难了。主要是不要往递归里面跳,就用我们平时的思考方式去理解好了。比如说,给你一个序列,求最大子列的和。我们正常的思考方式是:先知道左半部分的最大子列和以及右半部分的最大子列和,然后再知道中间部分的最大子列和,比较三种大小不就知道了整个序列的最大子列和了吗?而求左半部分和以及右半部分的最大子列也是用同样的方法,就拿左半部分为例:先知道左半部分的左半部分的最大子列和,以及左半部分的右半部分的最大子列和,然后再知道左半部分的中间部分的最大子列和......
AC代码如下:
1 #include <cstdio> 2 #include <algorithm> 3 4 int maxSubSeqSum(int *a, int low, int high); 5 6 int main() { 7 int n; 8 scanf("%d", &n); 9 int a[n]; 10 for (int i = 0; i < n; i++) { 11 scanf("%d", a + i); 12 } 13 printf("%d", maxSubSeqSum(a, 0, n - 1)); 14 15 return 0; 16 } 17 18 int maxSubSeqSum(int *a, int low, int high) { 19 // 递归的终止条件,子列只有1个数字 20 if (low == high) { 21 if (a[low] >= 0) return a[low]; 22 else return 0; 23 } 24 25 int mid = low + high >> 1; // 找到中分点 26 int leftMaxSum = maxSubSeqSum(a, low, mid); // 递归求得左半部分边子列的最大和 27 int rightMaxSum = maxSubSeqSum(a, mid + 1, high); // 递归求得右半部分边子列的最大和 28 29 int leftMax = 0, rightMax = 0, sum = 0; 30 // 从中线向左扫描 31 for (int i = mid; i >= low; i--) { 32 sum += a[i]; 33 if (sum > leftMax) leftMax = sum; 34 } 35 sum = 0; 36 // 从中线向右扫描 37 for (int i = mid + 1; i <= high; i++) { 38 sum += a[i]; 39 if (sum > rightMax) rightMax = sum; 40 } 41 42 // 三者中最大的那个就是整个序列的最大子列和 43 return std::max(std::max(leftMaxSum, rightMaxSum), leftMax + rightMax); 44 }
第三种解法是最快的,叫“在线处理”。
思路就是,只需要扫描整个序列一遍,累加每一项。如果发现某次累加的和比之前记录的最大和要大,则更新最大和;如果发现累加的和是负数,则当前的累加和置零,从下一项开始继续累加。
AC代码如下:
1 #include <cstdio> 2 3 int main() { 4 int n; 5 scanf("%d", &n); 6 int a[n]; 7 for (int i = 0; i < n; i++) { 8 scanf("%d", a + i); 9 } 10 int sum = 0, maxSum = 0; 11 for (int i = 0; i < n; i++) { 12 sum += a[i]; 13 if (sum > maxSum) maxSum = sum; 14 else if (sum < 0) sum = 0; 15 } 16 printf("%d", maxSum); 17 18 return 0; 19 }
浙江大学——数据结构:https://www.icourse163.org/course/ZJU-93001?tid=1461682474
标签:mat 一个 ott 整数 baseline 中间 == 解法 sub
原文地址:https://www.cnblogs.com/onlyblues/p/14811372.html