zoukankan      html  css  js  c++  java
  • 笔试算法题(44):简介

    议题:动态规划(Dynamic Programming)

    分析:

    • DP主要用于解决包含重叠子问题(Overlapping Subproblems)的最优化问题,其基本策略是将原问题分解为相似的子问题,通过求解并保存最简单子问题的解,然后逐步合并成为原问题的解,由于需 要查询子问题的解,所以需要一个表格记录子问题的解;DP仅适用于最优子结构问题(Optimal Substructure),也就是局部最优解相当于(或者近似于)全局最优解;

    • 对于原问题而言,当递归地自顶向下对问题进行求解时,每次产生的子问题可能与之前差生的子问题重复(如Fibonacci数列的求解);DP通过自底向上 求解子问题的解并将其保存在一个表格中,当再次遇到相同子问题时就直接通过查表得到其解(分治算法的子问题则是完全独立子空间);使用DP之前需要判定当 前问题是否具有下述三个特性:

      最优子结构:无论之前的状态和决策如何,当前的局部最优解是构成全局最优解的必要条件;

      无后效性:对于某个给定的阶段状态而言,在此之前的所有决策和状态都无法直接影响未来的决策,而只能通过当前的决策和状态影响;

      空间需求度:DP实际上是一种空间换时间的策略,在求解的过程中需要不断存储子问题的解并提供合理的查询方式;
    • DP的四个步骤

      划分阶段:注意划分之后的阶段必须是有序或者可排序的;

      确定状态和状态变量:将划分后的子问题使用不同状态表示,并满足无后效性;

      确定决策并写出状态转移方程:根据相邻两个阶段的状态关系得出转移方程;

      寻找边界条件:给出的转移方程是一个递归式,需要最终确定一个终止条件;

      最长公共子序列的DP解法

    • LCS问题与LIS(Largest Incremental Sub-sequence)问题类似,将原字符串A进行排序之后得到B,则A的LIS就是A和B的LCS。另外也可以直接使用DP;

    • 解法1:Largest Common Sub-string,如果将需求理解为公共子串的字符必须相连,则解法如下:将字符串A的每一个字符依次匹配B的每一个位置,时间复杂度O(MN),M 和N分别为A和B的长度,同样也可以使用DP填表的方式求解。KMP算法也可以求解,时间复杂度为O(M+N);

    • 解法2:Largest Common Sub-Sequence,如果将需求理解为公共子串的字符可以分离,则为经典的LCS问题(也可以理解为求两个集合的顺序交集),则解法如下:动态规划 (DP),动态规划一般应用于具有最优子结构的问题,也就是局部最优解可以决定全局最优解;动态规划的关键点在问题的拆分和状态关系的转移;

    • 给定first[1,m]和second[1,n],求LCS(first[1,m],second[1,n]),

      如果first和 second的最后一个字符相同,则有first[m]=second[n]=result[k],这样问题化解为给定first[1,m-1]和 second[1,n-1],求LCS(first[1,m-1],second[1,n-1]),原问题转化为 LCS(first[1,m],second[1,n])= LCS(first[1,m-1],second[1,n-1]) +1

      如果first和second的最后一个字符不相同,则问题化解为result[1,k]= max{LCS(first[1,m-1],second[1,n]), LCS(first[1,m],second[1,n-1])

    样例:

     1 /**
     2  * 利用动态规划,使用簿记matrix的方法记录小子问题,然后重复利用
     3  * 小子问题解决合成问题,最终解决整个问题。
     4  * 在first和second组成的二维表中,一共有三种状态转移方式:
     5  * 如果first[m]=second[n],则跳到first[m-1]和second[n-1]
     6  * 如果first[m]!=second[n],则跳到first[m-1]和second[n],
     7  * first[m]和second[n-1]的LCS中较大的一个
     8  * 需要设定初始状态为0
     9  * */
    10 void lcs2(char *first, int lfirst, char *second, int lsecond) {
    11         int *dir=new int[lfirst*lsecond];
    12         int *dis=new int[lfirst*lsecond];
    13         /**
    14          * 保留first和second的第一个字符,将其dis设置为0,便于实现簿记
    15          * dir矩阵中:0表示up-left移动;1表示left移动;2表示up移动
    16          * */
    17         for(int i=0;i<lfirst;i++)
    18                 dis[i]=0;
    19         for(int i=0;i<lsecond;i++)
    20                 dis[i*lfirst]=0;
    21 
    22         for(int j=1;j<lsecond;j++) {
    23                 for(int i=1;i<lfirst;i++) {
    24                         if(first[i]==second[j]) {
    25                                 /**
    26                                  * 如果当前字符相等,则说明[i,j]长度的LCS为
    27                                  * [i-1,j-1]长度的LCS 加上1;
    28                                  * up-left移动
    29                                  * */
    30                                 dis[i+j*lfirst]=
    31                                                 dis[(i-1)+(j-1)*lfirst]+1;
    32                                 dir[i+j*lfirst]=0;
    33                         } else if(dis[i+(j-1)*lfirst] >
    34                                                 dis[(i-1)+j*lfirst]) {
    35                                 /**
    36                                  * 如果当前字符不等,并且[i,j-1]长度的LCS大于
    37                                  * [i-1,j]长度的LCS,则当前[i,j]长度的LCS等于
    38                                  * [i,j-1]产度的LCS
    39                                  * up移动
    40                                  * */
    41                                 dis[i+j*lfirst]=
    42                                                 dis[i+(j-1)*lfirst];
    43                                 dir[i+j*lfirst]=2;
    44                         } else {
    45                                 /**
    46                                  * 如果当前字符不等,并且[i-1,j]长度的LCS大于
    47                                  * [i,j-1]长度的LCS,则当前[i,j]长度的LCS等于
    48                                  * [i-1,j]产度的LCS
    49                                  * left移动
    50                                  * */
    51                                 dis[i+j*lfirst]=
    52                                                         dis[(i-1)+j*lfirst];
    53                                 dir[i+j*lfirst]=1;
    54                         }
    55                 }
    56         }
    57 
    58         showLCS(first, dir, lfirst-1, lsecond-1, lfirst);
    59 
    60         delete [] dir;
    61         delete [] dis;
    62 }

    参考连接:
    http://blog.csdn.net/v_july_v/article/details/6695482
    http://en.wikipedia.org/wiki/Dynamic_programming
    http://blog.csdn.net/sharpdew/article/details/763180

  • 相关阅读:
    svnserve 配置
    JDBC与JTA的区别
    Redhat E5上安装Subversion 1.6详解
    CentOS5.3 编译 mod_jk 1.2.15 链接器 整合apache httpd 和 tomcat
    Linux对逻辑卷的创建与管理
    spring 包的解释
    vue.js之router详解(一)
    Ubuntu12.10 高速全自动配置bash脚本
    PHP execl导出/展示
    有关Linux下的一些配置
  • 原文地址:https://www.cnblogs.com/leo-chen-2014/p/3754514.html
Copyright © 2011-2022 走看看