题目描述:
给定有n个整数(可能为负整数)组成的序列a1,a2,...,an,求该序列连续的子段和的最大值。 如果该子段的所有元素和是负整数时定义其最大子段和为0。
输入
第一行有一个正整数n(n<1000),后面跟n个整数,绝对值都小于10000。直到文件结束。
输出
输出它的最大子段和。
样例输入
6 -2 11 -4 13 -5 -2 样例输出 20
主要思想:
总序列的最大子段和有三种情况:1)与前段相同。2)与后段相同。3)跨前后两段。
利用分治的思想,将整个数组a[p:q]首先划分成a[p:(p+q)/2] 和 a[(p+q)/2+1:q]这两个区域,但是要求这两个区域的最大子段还是需要划分,所以可以再继续分,一直分到只剩下一个元素为止,然后再开始从这几个子段中找到中心,向两边开始找最大的子段,一步步往上开始找,每个子段寻找到的最大子段都要上面三种情况进行比较来得到一个最大的子段。
分治一般思想:
分治法解题的一般步骤:
(1)分解,将要解决的问题划分成若干规模较小的同类问题;
(2)求解,当子问题划分得足够小时,用较简单的方法解决;
(3)合并,按原问题的要求,将子问题的解逐层合并构成原问题的解。
#include <iostream>
#include <string.h>
using namespace std;
int f(int a[],int low, int high)
{
int mid;
int sum = 0;//记录最大子段
int leftsum = 0;//记录左面的最大子段
int rightsum = 0;//记录右面的最大子段
int rs = 0;//记录中间部分右面子段的最大子段
int ls = 0;//记录中间部分左面的最大子段
int lefts = 0;//暂时记录从mid到最左面的数据之和
int rights = 0;//暂时记录从mid到最右面的数据之和
if (low == high)
{
if (a[low] > 0)
sum = a[low];
else
sum = 0;
}
if (low < high) {
mid = (low + high)/2;
leftsum = f(a, low, mid);//不断去寻找左面最大子段
rightsum = f(a, mid + 1, high);//不断去寻找右面最大子段
for (int i = mid ; i >= low; i--) {
lefts += a[i];
if (ls < lefts)
ls = lefts;
}
for (int j = mid + 1; j <= high; j++) {
rights += a[j];
if (rs < rights)
rs = rights;
}
sum = ls + rs;//sum保存跨前后两段情况的最大子段和
}
if (sum < rightsum)
sum = rightsum;//记录右面的子段为最大子段
if (sum < leftsum)
sum = leftsum;//记录左面的子段为最大子段
return sum;
}
int main()
{
int n;
int num[1000];
while (cin >> n)
{
memset(num, 0, sizeof(num));
for (int i = 0; i < n; i++)
{
cin >> num[i];
}
cout << f(num, 0, n - 1) << endl;
}
return 0;
}
转发链接:https://blog.csdn.net/zhong36060123/article/details/4381391(最大子段和详解(N种解法汇总))