算法第二章上机实践报告

1.实践题目名称 

  7-1 最大子列和问题 

2.问题描述

  给定K个整数组成的序列{ N1​​, N2​​, ..., NK​​ },“连续子列”被定义为{ Ni​​, Ni+1​​, ..., Nj​​ },其中 1ijK。“最大子列和”则被定义为所有连续子列元素的和中最大者。例如给定序列{ -2, 11, -4, 13, -5, -2 },其连续子列{ 11, -4, 13 }有最大的和20。现要求你编写程序,计算给定整数序列的最大子列和。

3.算法描述

  一个序列的最大连续子段和,可能在该序列的左序列、右序列或中序列。用分治法解决。首先,分解子问题:把一个序列分解成三个子序列来解决,分别是左序列,右序列,和中序列。其次,求解“左序列的最大子段和” 与 “求解右序列”又可以看成是“和原问题相同”的子问题,用递归函数来实现求解;中序列则需要特殊考虑,可以从中间的那个元素分别向左、向右求和,最后把两个和加起来,就是中序列的最大子段和。最后,左、中、右序列的最大子段和,三者中最大的就是答案。

具体代码如下:

 1 #include<iostream>
 2 using namespace std;
 3 
 4 int MaxSum(int a[],int left,int right)
 5 {
 6     int maxsum = 0;
 7     
 8     //递归的变界条件
 9     if(left == right){
10         if(a[left] > 0){
11             return a[left];
12         }
13         else{
14             return 0; //如果序列中所有整数皆为负数,则输出0
15         }
16     }
17     else{
18         int mid = (left + right) / 2;
19         int leftsum = MaxSum(a, left, mid); //求左序列最大子段和 
20         int rightsum = MaxSum(a, mid+1, right); //求右序列最大子段和 
21         
22         //求中间序列最大子段和 
23         int left_border_sum = 0;
24         int s1 = 0; //临时累加器1 
25         for(int i = mid; i >= left; i--)
26         {
27             s1 += a[i];
28             if(s1 > left_border_sum)
29             {
30                 left_border_sum = s1;
31             }
32         }
33         int right_border_sum = 0;
34         int s2 = 0; //临时累加器2 
35         for(int i = mid+1; i <= right; i++)
36         {
37             s2 += a[i];
38             if(s2 > right_border_sum)
39             {
40                 right_border_sum = s2;
41             }
42         }
43             
44         int midsum = left_border_sum + right_border_sum;
45         
46         maxsum = leftsum > rightsum ? leftsum : rightsum;
47         maxsum = midsum > maxsum ? midsum : maxsum;
48         
49         return maxsum;
50     } 
51      
52 }
53 
54 int main()
55 {
56     int K,a[200005];
57     cin>>K;
58     for(int i = 0; i < K; i++)
59     { 
60         cin>>a[i];
61     }
62     
63     cout<<MaxSum(a, 0, K-1);
64     return 0;
65 }

 

4.算法时间及空间复杂度分析(要有分析过程)

一、时间复杂度分析:

  ①分解子问题:将原序列一分为二,时间复杂度为O(1)

  ②求解子问题:子问题的规模,是原来问题规模的一半;原问题分解为两个相同的子问题。所以时间复杂度为2T(n/2)

  ③合并子问题的解:每一次递归调用,就合并一次子问题的解。一共需要递归调用n/2次,所以时间复杂度为O(n)

  解此递归方程可得,T(n) = O(nlogn)

二、空间复杂度分析:

  空间复杂度为O(n),用于存储输入数据

5.心得体会(对本次实践收获及疑惑进行总结)

  本次实践过程中对“子问题划分的边界值”产生了疑惑,这涉及到序列的个数是奇数还是偶数。一开始我写的是:

int leftsum = MaxSum(a, left, mid-1); //求左序列最大子段和 
int rightsum = MaxSum(a, mid, right); //求右序列最大子段和 

  这样子会产生错误。举个例子:若序列的只有两个,则left=0,right=1,mid=0。此时划分出的左序列就是从0到-1,而右序列是从0-1。不仅是左序列的划分变界可能会出错,而且右序列也没有缩小问题规模的大小。

  这涉及到mid的向上取整还是向下取整的问题。正确写法为:

int leftsum = MaxSum(a, left, mid); //求左序列最大子段和 
int rightsum = MaxSum(a, mid+1, right); //求右序列最大子段和

  通过这道题,我对分治思想有了更深刻的理解,对分治法中的“划分子问题”还需要更好的把握。

posted @ 2020-10-03 19:45  z-qiong  阅读(133)  评论(0编辑  收藏  举报