zoukankan      html  css  js  c++  java
  • 0x07算法设计与分析复习(二):算法设计策略-动态规划法1

    参考书籍:算法设计与分析——C++语言描述(第二版)

    算法设计策略-动态规划法

    动态规划法

    动态规划算法是另一种求解最优化问题的重要算法设计策略。对于一个问题,如果能从较小规模的子问题的最优解求得较大规模同类子问题的最优解,最终得到给定问题的最优解,这就是最优解的最优子结构特性。最优子结构特性使动态规划算法可以采用自底向上的方式进行计算,如果能在求解中保存已计算的子问题的最有解,当这些子最优解被重复引用时,则无须再次计算,从而节省了大量的计算时间。

    动态规划法与分治法和贪心法的比较

    动态规划算法的实质也是将较大的问题分解为较小的同类子问题,在这一点上它与分治法和贪心法类似。但动态规划法有自己的特点。分治法的子问题相互独立,相同的子问题被重复计算,而动态规划法解决了这种子问题重叠现象。贪心法要求针对问题设计最优量度标准,但这在很多情况下并不容易做到,而动态规划法利用最优子结构,自底向上从子问题的最优解逐步构造出整个问题的最优解,动态规划法可以处理不具备贪心准则的问题。

    贪心算法在求解问题的每一步上根据最优量度标准做出某种决策,产生n-元组的一个分量。用于决策的贪心准则仅依赖于局部和以前做出的选择,但决不依赖将来的选择,也不依赖于子问题的解。

    动态规划法每一步的决策依赖于子问题的解。为了在某一步上做出选择,需要先求解若干个子问题,再根据子问题的解做出决策,这就使得动态规划法求解问题的方法使自底向上(bottom-up)的。

    当一个问题的最优解中包含了子问题的最优解时,则称该问题具有最优子结构特性(optimal substructure)。最优子结构特性是动态规划算法求解问题的必要条件,最优子结构特性也叫做最优性原理(principle of optimality)

    按照多步决策的方法,一个问题的活动过程可以分为若干个阶段(stage),每一个阶段可包含一个或多个状态(state)。也就是说,一个活动过程进展到某一阶段时,可以处于该阶段的某一个状态。多步决策求解方法是从初始阶段的初始状态出发,做出每一个阶段的决策(decision),形成一个决策序列(sequence of decision),该决策序列也称为一策略(policy)。对于每一个决策序列,可以用一个数值函数(即目标函数)衡量该决策的优劣。问题求解的目标是获取导致问题最优解的最优决策序列(optimal sequence of decision),也称最优策略

    最优性原理指出,一个最优策略具有这样的性质,不论过去状态和决策如何,对前面的决策所形成的状态而言,其余决策必定构成最优策略。这便是最优决策序列的最优子结构特性。

    分治法将问题分解成若干个相互独立的子问题,但一个子问题分解所得到的子问题并不总是完全相互独立的,他们可能共享更小的子问题,被称为重叠子问题(overlapping subproblem)。动态规划法实施自底向上计算,并保存子问题的解,从而可以避免重复计算这些重叠子问题。

    设计一个动态规划算法,通常可以按照以下4个步骤进行:

    1. 刻画最优解的结构特性;
    2. 递归定义最优解值;
    3. 以自底向上方式计算最优解值;
    4. 根据计算的得到的信息构造一个最优解。

    第1至3步是动态规划算法的基本步骤。最优解值是最优解的目标函数的值。

    基本要素

    一个最优化多步决策问题是否适合用动态规划法求解有两个要素:最优子结构特性和重叠子问题。

    最优子结构特性

    最优子结构特性: 即问题的最优解包含了其子问题的最优解。

    分析(证明)问题的最优子结构特性时:首先假设由问题的最优解导出的子问题的解不是最优的,然后再设法说明在这个假设下可构造出比原问题最优解更好的解,从而导致矛盾。

    利用问题的最优子结构特性,以自底向上的方式递推地从子问题的最优解逐步构造出整个问题的最优解。最优子结构是问题能用动态规划算法求解的前提。

    子问题重叠特性

    子问题重叠特性:递归算法求解问题时,每次产生的子问题并不总是新问题,有些子问题被反复计算多次。

    动态规划算法,对每一个子问题只解一次,而后将其解保存在一个表格(通常用二维数组)中,当再次需要解此子问题时,只是简单地用常数时间查看一下结果。

    通常不同的子问题个数随问题的大小呈多项式增长。因此用动态规划算法只需要多项式时间,从而获得较高的解题效率。

    多段图问题

    多段图问题

    多段图G=(V,E)是一个带权有向图,它具有如下特征:图中的结点被划分成k2个互不相交的子集Vi,1ik。其中V1Vk分别只有一个结点,V1包含源点(source)sVk包含汇点(sink)t。对所有边<u,v>E,多段图要求若uVi,则vVi+1(1i<k),边上的权值为w(u,v)。从s到t的路径长度是这条路径上边的权值之和。多段图问题(multistage graph problem)是求从s到t的一条长度最短的路径。

    下图是一个5段图,共分为5个阶段,每个阶段包含多个状态,每一个结点代表一个状态。每一条从s到t的路径都可以看作是由每一个阶段做出的决策组成的决策序列所产生的结果。每条路径上的所有边的权值之和称为路径长度,这是问题的目标函数值。

    这里写图片描述

    多段图的最优子结构

    对于上述多段图,可以证明它的最优解满足最优子结构特性。如果(s,v2,v3,,vk1,t)是一条从s到t的最短路径,那么(v2,v3,,vk1,t)必定是一条从v2到t的最短路径。(若不然,另有(v2,q3,,vk1,t)是一条从v2到t的最短路径,那么路径(s,v2,q3,,vk1,t)显然比(s,v2,v3,,vk1,t)更短,与假设矛盾。)这就说明对于多段图问题,最优性原理成立。

    多段图的向前递推关系式

    由于多段图问题具有最优子结构特性,启发我们从最后阶段开始,采用逐步向前递推的方式,由子问题的最优解来计算原问题的最优解。由于动态规划法的递推关系是建立在最优子结构的基础上的,相应的算法容易用归纳法证明其正确性。

    多段图问题的向前递推式:

    cost(k,t)=0cost(i,j)=minjVi,pVi+1<j,p>E{c(j,p)+cost(i+1,p)}, 1ik1

    其中,cost(i,j)是从第i阶段j到t的最短路径的长度,i是阶段编号,j是第i阶段的一个状态(结点)编号。

    以上图的5段图,使用递推式,由后向前计算最优解值的步骤如下:

    cost(5,10)=0,cost(4,10)=5,cost(4,9)=2,cost(4,8)=4,cost(3,7)=min{6+cost(4,10),5+cost(4,9)}=7,cost(3,6)=5,cost(3,5)=7,cost(2,4)=min{8+cost(3,7),11+cost(3,6)}=16,cost(2,3)=18,cost(2,2)=9,cost(2,1)=7,cost(1,0)=min{9+cost(2,1),7+cost(2,2),3+cost(2,3),2+cost(2,4)}=16

    cost(1,0)即为多段图问题的最优解值。

    为得到具体的路径,我们可以定义d(i,j)来记录从第i阶段结点j到t的最短路径上该结点的下一个结点编号。很容易从d的值确定最短路径上的点。(0,d(1,0)=1,d(2,1)=6,d(3,6)=9,d(4,9)=11,11)

    多段图的动态规划算法

    //多段图的向前推进算法
    template<class T>
      T Graph<T>::FMultiGraph(int k,int *p)
      {
        //采用邻接表存储图G
        Tc *cost=new float[n];
        int q, *d=new int[n];
    
        cost[n-1]=0,d[n-1]=-1;//设置向前推进的初始值
        for(int j=n-2;j>=0;j--){
          //按n-2,...,0的次序计算cost和d
          float min=INFTY;
          //计算最小值的cost[j]
          for(ENode<T> *r=a[j];r;r=r->nextArc){
            int v=r->adjVex;
            if(r->w+cost[v]<min){
              min=r->w+cost[v];
              q=v;
            }
          }
          cost[j]=min;
          d[j]=q;//q是j再最短子路径上的后继结点
        }
        p[0]=0;//p[0]和p[n-1]是源点和汇点
        p[k-1]=n-1;
        c=cost[0];
        for(j=1;j<=k-2;j++){
          p[j]=d[p[j-1]];
          //p[i]是最短路径上第i阶段的点
        }
        delete []cost;
        delete []d;
        return c;
      }

    该算法,使用cost[j]保存结点j到t的最短路径,数组p保存对应于cost[0]的最短路径上的结点,它是问题的最优解。算法的时间复杂度为Θ(n+e)

    多段图的向后递推关系式

    k段图问题也可以向后地推求解,其递推公式如下:

    Bcost(1,s)=0Bcost(i,j)=minpVi1,jVi<p,j>E{c(p,j)+Bcost(i1,p)}, 1<ik

    式中,Bcost(i,j)是源点到第i阶段状态j的最短路径的长度,i是阶段编号,j是第i阶段的一个状态(结点)编号。

    小结

    动态规划法与分治法

    共同点:

    将待求解的问题分解成若干子问题,先求解子问题,然后再从这些子问题的解得到原问题的解
    不同点:

    1. 适合于用动态规划法求解的问题,分解得到的各子问题往往不是相互独立的;而分治法中子问题相互独立。
    2. 动态规划法用表保存已求解过的子问题的解,再次碰到同样的子问题时不必重新求解,而只需查询答案,故可获得多项式级时间复杂度,效率较高;而分治法中对于每次出现的子问题均求解,导致同样的子问题被反复求解,故产生指数增长的时间复杂度,效率较低。

    动态规划法与贪心法

    共同点:

    都是求解最优化问题;都要求问题具有最优子结构性质。

    不同点:

    1. 求解方式不同:
      • 动态规划法:自底向上;
      • 贪心法:自顶向下。以迭代的方式作出相继的贪心选择,每作一次贪心选择就将所求问题简化为一个规模更小的子问题。
    2. 对子问题的依赖不同:
      • 动态规划法:依赖于各子问题的解,所以只有在解出相关子问题后,才能作出选择;应使各子问题最优,才能保证整体最优;
      • 贪心法:不依赖于子问题的解。仅在当前状态下作出最好选择,即局部最优选择,然后再去解作出这个选择后产生的相应的子问题。

    动态规划法是求解最优化问题的一种重要的算法设计技术。动态规划法的基本要素是最优子结构和重叠子问题。一个问题的最优解具有最优子结构,便可用动态规划法求解。动态规划法同样运用问题分解思想,但与分治法不同的是它采用自底向上计算方式,共享子问题无须重复计算。备忘录方法是动态规划法的一个变种,它采用自顶向下的方式计算最优解,但与分治法不同的是,备忘录方法为每个已经计算的子问题建立备忘录,从而避免了相同子问题的重复求解。

  • 相关阅读:
    公开课 | 高段位的测试开发工程师是如何养成的?
    测试面试真题 | 从屡次碰壁到成长蜕变,半年拿下某大厂 60W 年薪测试开发 Offer!
    Git实战(五)| 让工作更高效,搞定Git的分支管理
    双 11 钜惠 | 测试开发爆款课程全年最低价,送 Mate40Pro、Kindle、华为手环等万元豪礼!
    测试面经 | 从测试螺丝钉到大厂测试开发,三点成长心得和面试经验
    Sql Server 常用函数
    IE Web Control介绍以及TreeView 控件树形结构不能显示的问题
    一个javascript脚本写的时钟
    使用DataGrid控件实现主细表
    解决 SQL Server 耗尽内存的情况
  • 原文地址:https://www.cnblogs.com/born2run/p/9581374.html
Copyright © 2011-2022 走看看