• 重看算法 -- 动态规划 最大子段和问题


     

    问题描述:

    给定长度为n的整数序列,a[1...n], 求[1,n]某个子区间[i , j]使得a[i]+…+a[j]和最大,或者求出最大的这个和。如果该序列的所有元素都是负整数时定义其最大子段和为0。

    例如(-2,11,-4,13,-5,2)的最大子段和为20,所求子区间为[2,4]。

    问题分析:

    最直接的想法就是利用遍历法遍历所有的可能,然后找到最大的那个,显然这不是一种有效的方法,但切实可行。在第二章的时候学习了分治方法,想到也可以把序列拆分成两部分,答案就在前半段或者后半段或者是穿过两段中间的部分。

    暴力遍历法:

    就是找到所有可能的结果然后再判断找到符合要求的那一个。首先我们需要一个循环来遍历从第一个位置到最后一个位置:for(int i = 0;i < n; i++),然后还需要一个内层循环来遍历从当前位置到最后一个位置,来分别计算当前的最大子段和:

     1 int maxSum(int n, int[] a, int besti, int bestj) {
     2     int sum = 0;
     3     for (int i = 1; i <= n; i++) {
     4         int thissum = 0;        
     5         
     6         for (int j = i; j <= n; j++) {
     7             thissum += a[j - 1];
     8             if (thissum > sum) {
     9                 sum = thissum;
    10                 besti = i;
    11                 bestj = j;
    12             }   // end if
    13         }   // end inner for
    14         
    15     }   // end out for
    16     
    17     return sum;
    18 }   // end maxSum

    很明显该算法的计算时间是O(n²)。

    分治法:

    针对最大字段和这个具体问题本身的结构,还可以从算法设计的策略上对上述O(n²)计算时间算法加以更深刻的改进。

    如果将给定的序列a[1..n]分成长度相等的两段a[1..n/2]和a[n/2+1:n],分别求出这两段的最大字段和。则该给定序列的最大字段和有三种情行:

    • ①和a[1..n/2]的最大字段和相同。
    • ②和a[n/2+1:n]的最大字段和相同。
    • ③最大字段和包含两部分,一部分在a[1..n/2]中,另一部分在a[n/2+1..n]中。

    前两种情形我们可以用递归方法求出,第三种情形可以分别求出两部分的最大字段和值再相加(注:a[1..n/2]这部分求最大字段和要以a[n/2]结束,a[n/2+1..n] 这部分求最大字段和要以a[n/2+1]开始)。序列的最大字段和即为这三种情形的最大值

     1 static int maxSubSum(int[] a, int left, int right) {
     2     int sum = 0;
     3     if (left == right) {
     4         sum = a[left - 1] > 0 ? a[left - 1] : 0;
     5     } else {
     6         int center = (left + right) / 2;
     7         int leftSum = maxSubSum(a, left, center);
     8         int rightSum = maxSubSum(a, center + 1, right);
     9 
    10         int s1 = 0;
    11         int lefts = 0;
    12         for (int i = center; i >= left; i--) {
    13             lefts += a[i - 1];
    14             if (lefts > s1) s1 = lefts;
    15         }
    16 
    17         int s2 = 0;
    18         int rights = 0;
    19         for (int i = center + 1; i <= right; i++) {
    20             rights += a[i - 1];
    21             if (rights > s2) s2 = rights;
    22         }
    23 
    24         sum = s1 + s2;
    25         if(sum < leftSum) sum = leftSum;
    26         if(sum < rightSum) sum = rightSum;
    27     }   // end if
    28 
    29     return sum;
    30 }   // end maxSubSum

    该算法的计算时间为O(nlogn)。

    动态规划算法:

    如果我们定义一个b[j]表示到当前位置为止,最大的字段和,那么事情就会变得更加简单:

     1 static int maxSum(int n, int[] a) {
     2     int sum = 0, b = 0;
     3     for (int i = 1; i <= n; i++) {
     4         if (b > 0) b += a[i - 1];
     5         else b = a[i - 1];
     6         if (b > sum) sum = b;
     7     }
     8 
     9     return sum;
    10 }

    该算法的计算时间需要O(n)。




    转载自:https://www.jianshu.com/p/69e51c3a36eb

    认清现实,放弃幻想。 细节决定成败,心态放好,认真学习与工作。
  • 相关阅读:
    SQL Server数据库新建拥有部分查看操作权限的用户
    Asp.net导入Excel数据文件
    前台页面下载服务器端文件
    页面开机自启动,页面置顶显示,页面持续获得焦点,鼠标点击器源码
    asp.net DataGrid GridView 表格之分页显示与翻页功能及自定义翻页页码样式
    asp.net DataGrid GridView 表格之取消设计最初显示的绑定列
    asp.net DataGrid GridView 表格之选中行与获取选中行数据
    Winform 、asp.net TreeView 树形控件
    Torrent种子下载下来的文件,如何校验其完整性?
    在与 SQL Server 建立连接时出现与网络相关的或特定于实例的错误
  • 原文地址:https://www.cnblogs.com/jyf2018/p/14785249.html
Copyright © 2020-2023  润新知