[分治、递推法、动态规划]最大子段和

最大子段和

 

例题:http://acm.sdut.edu.cn/onlinejudge2/index.php/Home/Contest/contestproblem/cid/3015/pid/3664

题目解析:最大子段和,从三种角度来解决。


 

算法一:分治算法

算法思路:

1. 我们先来考虑一下如何求 [2,-1,2] / [2,-4,3] 这两个的最大子段。

  第一个的最大子段,是 2+(-1)+2;第二个的最大子段是 3。

2. 分治算法处理:

  我们把一个大问题分为很多小问题,而大化小最好的办法是二分法。

  

  看上图,我们一共求三块,前两块(MaxL,MaxR)是递归求解出来的真正的左边最大子段,右边最大子段;而Max是判断这两个子段是否能合并在一起。

  如果能合并在一起,则Max=MaxL+MaxR,否则是独立的。

  你应该明确一个前提,左边最大,右边最大,连接起来并不一定最大,举个极端的例子 [ 1,-1,-1,-1,-1,-1,1]。如果否定前提,则这个子段最大和应该为2,但显然是错误的。

  实际上:左边最大,右边最大,则它们和可能更小,可能更大,也可能一样;其取决于连接在一起之后的结果。

  解决方案:从[l <-- mid],试探性相加,每次相加判断是否满足最大,如果是则记录,直到最后则求出的肯定是"从mid开始到l的最大子段"(并非最大子段,是从mid开始,因为要连接在一起连接口必须在mid),右边同理。

        左右都计算出来结果为Max,然后分别与MaxR与MaxL比较(并非MaxR+MaxL,因为我们之前强调过这个思想是错误的)。

源代码:

 1 // 算法.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
 2 //
 3 
 4 #include "pch.h"
 5 #include <iostream>
 6 #include <map>
 7 #include <math.h>
 8 #include <algorithm>
 9 using namespace std;
10 
11 int a[100000];
12 int cnt;
13 
14 int MaxSegment(int l,int r){
15     cnt++;
16     int sum = 0;
17     if (l == r) {
18         if (a[l] >= 0)sum = a[l];
19         else sum = 0;
20     }
21     else {
22         int mid = (l + r)/ 2;
23         int MaxL = MaxSegment(l, mid);
24         int MaxR = MaxSegment(mid+1, r);
25         int s1 = 0,s2=0, ss = 0;
26         // 开始向左计算
27         for (int i = mid; i >=l;i-- ) {
28             ss += a[i];
29             if (ss > s1)s1 = ss;
30         }
31 
32         // 开始向右计算
33         ss = 0;
34         for (int i = mid + 1; i <= r; i++) {
35             ss += a[i];
36             if (ss > s2)s2 = ss;
37         }
38 
39         sum = s1 + s2;
40         sum = max(sum, MaxL);
41         sum = max(sum, MaxR);
42     }
43     return sum;
44 }
45 int main() {
46     int n;
47     cin >> n;
48     for (int i = 0; i < n; i++)
49         scanf_s("%d",&a[i]);
50     int Max = MaxSegment(0, n - 1);
51     printf("%d %d", Max, cnt);
52 }

 


算法二:递推法

 

posted @ 2019-10-13 17:07  OneTrainee  阅读(560)  评论(0编辑  收藏  举报