zoukankan      html  css  js  c++  java
  • 算法导论笔记:04最大子数组矩阵乘法以及递归式求解

           最大子数组,给定数组,在这个数组中找到这样的子数组:子数组的和是所有子数组中最大的(子数组必须是连续的)。

     typedef  struct
    {
           int  max;
           int  subbegin;
           int  subend;
    }SUBMAX;

    #define  NEINFINITE  (0x80000000)
    #define  true      1
    #define  false     0

           1:最普通的解法,根据定义,求所有子数组的和,然后找到最大子数组。时间复杂度为O(n^2)

           从第i个元素开始,连续求得以i为起点,i,i+1,i+2...为终点的子数组的和,得到以i为起点的所有子数组的最大值,然后对于i,0<i<num。代码如下:

    SUBMAX normalmaxsub(int *set,  int  num)

    {

             int i, j;

             int begin, end;

             int max = NEINFINITE;

             int and = 0;

             SUBMAX res;

            

             for(i = 0; i < num; i++)

             {

                      and = set[i];

                      if(max < and)

                      {

                              max = and;

                              begin = i;

                              end = i;

                      }

                      for(j = i+1; j < num; j++)

                      {

                              and = and + set[j];

                              if(max < and)

                              {

                                       max = and;

                                       begin = i;

                                       end = j;

                              }

                      }

             }

             res.max = max;

             res.subbegin = begin;

             res.subend = end;

             return res;

    }

     

    2:分治算法:将原数组分解为左右两部分,mid为分界点,那么原数组的最大子数组的位置只有3中可能:

           a:在mid左边的子数组中;

           b:在mid右边的子数组中;

           c:穿过中间节点mid的子数组。

     

           因找穿过中间节点的最大子数组的时间复杂度是O(n)。所以,该算法的时间复杂度:T(N) = 2T(N/2) + O(N),即为O(nlgn)。代码如下:

    SUBMAX  dividemaxsub(int *set,  int  begin,  int  end)

    {

             int mid;

             SUBMAX leftmax, rightmax, midmax;

             leftmax.max = rightmax.max = midmax.max= NEINFINITE;

     

             SUBMAX max;

            

             if(begin == end)

             {

                      max.max = set[begin];

                      max.subbegin = begin;

                      max.subend = begin;

                      return max;

             }

            

             if(begin < end)

             {

                      mid = (begin + end)/2;

                      if(mid < end)

                      {

                              rightmax = dividemaxsub(set, mid+1, end);

                      }

                      if(mid > begin)

                      {

                              leftmax = dividemaxsub(set, begin, mid-1);

                      }

                      midmax = crossmidmaxsub(set, begin, end, mid);

                      max = (leftmax.max < rightmax.max)?rightmax:leftmax;

                      max = (max.max < midmax.max)?midmax:max;

                      return max;

             }

    }

     

    SUBMAX crossmidmaxsub(int *set,  int  begin,  int  end,  int  mid)

    {

             int i,j;

             int max = 0;

             int and = 0;

             SUBMAX res;

     

             and = set[mid];

             max = and;

            

             res.subbegin = mid;

             res.subend = mid;

            

             for(i = mid - 1; i >= begin; i--)

             {

                      and = and + set[i];

                      if(max < and)

                      {

                              max = and;

                              res.subbegin = i;

                      }

             }

            

             and = max;

            

             for(j = mid + 1; j <= end; j++)

             {

                      and = and + set[j];

                      if(max < and)

                      {

                              max = and;

                              res.subend = j;

                      }

             }

             res.max = max;

             return res;

    }

     

    3:DP解法,时间复杂度为O(n):

           对于数组a[0…i],若已知它的最大子数组,如何求a[0…i+1]的最大子数组?

           先看数组a[0…i],已知它的最大子数组,记为res(i),同时,也可求得包含元素a[i]的最大子数组。记为sum(i)

           对于数组a[0…i+1],它的最大子数组,要么包含元素a[i+1],要么不包含元素a[i+1]

           包含元素a[i+1]的情况是:res(i+1)= sum(i+1)

           不包含元素a[i+1]的情况是res(i+1)= res(i)

     

           现在的问题就是如何求解sum(i+1):如果sum(i) > 0,则sum(i+1) = sum(i) + a[i+1],否则,sum(i+1) = a[i+1]。

     

           这样,依次遍历数组,每遍历一个元素,就可求得sum(i)。具体代码如下:

    SUBMAX  DPmaxSum(int *a, int length)

    {

             int res =NEINFINITE;

             int sum = 0;

             int i;

     

             int sumbegin = 0;

             int sumend = 0;

             int resbegin = 0;

             int resend = 0;

            

             SUBMAX result;

             for(i = 0; i < length; i++)

             {

                      if(sum >= 0)

                      {

                              sum = sum + a[i];

                              sumend = i;

                      }

                      else

                      {

                              sum = a[i];

                              sumbegin = sumend = i;

                      }

                      res = (sum > res)?sum:res;

                      if(res == sum)

                      {

                              resbegin = sumbegin;

                              resend = sumend;

                      }

             }

             result.max = res;

             result.subbegin = resbegin;

             result.subend = resend;

             return result;

    }

     

     

           1:普通的矩阵乘法就是依次扫描两个矩阵的行和列,然后计算结果矩阵的每一个元素,时间复杂度为Ө(n^3)

     

           2:分治法求解矩阵的乘积,就是把一个矩阵依次分为4个部分,对每个部分分别求解。该算法的递归式为:

    该算法的时间复杂度为Ө(n^3)

     

           3:Strassen方法:递归方法需要8次乘积,Strassen方法采用矩阵的加减法减少了乘机次数,也就是7次乘积,所以,该算法的时间复杂度为Ө(n^lg7)

     

           4:分治模式在每层递归时都有三个步骤:

    分解:原问题为若干子问题,这些子问题是原问题的规模较小的实例。

    解决:这些子问题,递归地求解各子问题。然而,若子问题的规模足够小,则直接求解。

    合并:这些子问题的解成原问题的解。

     

           分治算法运行时间的递归式来自基本模式的三个步骤。如前所述,我们假设T(n)是规模为n的一个问题的运行时间。若问题规模足够小,如对某个常量c,n≤c,则直接求解需要常量时间,我们将其写作Θ(1)。假设把原问题分解成a个子问题,每个子问题的规模是原问题的1/b。(对归并排序,ab都为2,然而,我们将看到在许多分治算法中,ab。)为了求解一个规模为n/b的子问题,需要T(n/b)的时间,所以需要aT(n/b)的时间来求解a个子问题。如果分解问题成子问题需要时间D(n),合并子问题的解成原问题的解需要时间C(n)那么得到递归式:


     

           如何求解递归式,有3中方法,代入法(先猜测,然后数学归纳法证明),画递归树(可以猜测结果,然后用代入法求解),主定理。

           5:递归式主定理:

  • 相关阅读:
    基于NFS的PV动态供给(StorageClass)
    Helm学习笔记
    k8s日志收集方案
    okhttputils【 Android 一个改善的okHttp封装库】使用(三)
    PopupWindowMenuUtil【popupwindow样式菜单项列表】
    NewBuiltBottomSheetDialog【新建底部对话框】
    NormalDialogFragmentDemo【普通页面的DialogFragment】
    ArticleRemoveDelDialog【基于AlertDialog的回收删除对话框】
    ConfirmCancelBottomSheetDialog【确认取消底部对话框】
    Android-PickerView【仿iOS的PickerView控件,并封装了时间选择和选项选择这两种选择器】使用
  • 原文地址:https://www.cnblogs.com/gqtcgq/p/7247244.html
Copyright © 2011-2022 走看看