动态规划几个经典例子总结

来源:互联网 发布:如何开淘宝充值店 编辑:程序博客网 时间:2024/05/02 07:14

1.了解动态规划 

   写的不是特别详细,凑合看吧,嘿嘿, 快要考试了,攒人品....

动态规划的核心思想就是避免子问题重复计算,采用用空间换取时间的方法提高算法效率,比如用递归实现的斐波那契数与用数组记录子问题实现的递推算法就是最简单的动态规划思想,用表的形式记录子问题,防止重复求解,类似的例子也使用与其他递归公式,难点在于最优子结构性质的发现与证明,通常找出最优子结构后都能写出递推表达式,从而写出动态规划的算法.

动态规划的解题方法:

如果存储解的数组是二维的,或可用二维数组解决的,通常可以画出二维矩阵,行坐标与竖坐标分别对应问题中实际意义.举一个简单的实际的带数据的例子.按照最优子结构(如果还没找到,就根据问题要求),自底向上的求解,最后通常能准确的写出递推关系.


2.典型动态规划例题

   2.1 0-1背包问题

   2.1-1.问题描述:

   一共n中物品,每样物品只有一件,物品i的重量为wi>0,价值为vi,背包的最终容量为weight,求如何添加物品,在不超过背包容量的情况下,背包中物品的价值最大?

    分析:   这是一道典型的动态规划题目,当然可以用回溯法枚举每一种可能,最后求出最大值,但是回溯法的搜索空间为2^n,即每一种物品都有选和不选之分,选择该物品为1,为右子树,不选该物品为0,在为左子树,这样的一个搜索空间为2^n,复杂度为O(2^n).  由于0-1背包问题很显然含有很多子问题,画出回溯法时的解空间树就可以知道有很多重叠的子树,因此我们考虑用动态规划方法求解.


    2.1-2.动态规划方法,找出最优子结构

    设Z={z1, z2, z3, ....,zn}是一个最优解,其中zi=1表示背包中含有该物品,zi=0表示背包中不含该物品.0-1背包问题Knap(n, weight)在此时的价值为dp[n][weiht].下面为0-1背包的最优子结构.

   (1)  如果zn=1,那么dp[k-1][weight - w[n]] + v[n] > dp[n-1][weight],而且{z1,z2,…,zn-1}是Knap(n-1,weight- w[n])的最优解.(最优解包含子问题的最优解)
   (2)  如果zk=0,那么dp[n-1][weight - w[n]) + v[n] <= dp[n-1][weight],而且{z1,z2,…,zn-1}是Knap(n-1,weight)的最优解。
   证明:
   (1)如果dp[k-1][weight - w[n]] + v[n] <= dp[n-1][weight],则有dp[n][weight]= dp[n-1][weight - w[n]] + v[n] <= dp[n-1][weight],从而得出Z={z1,z2,…,zn}不是最优解,与前提矛盾。因此dp[n-1][weight - w[n]] + v[n] > dp[n-1][weight]。假设{z1,z2,…,zn-1}不是Knap(n-1,weight- w[n])的最优解,则存在一个解使得f[n-1][weight] > dp[n-1][weight],则f[n-1][weight]+ v[n] > dp[n-1][weight - w[n]] + v[n] = dp[n][weight],与假设矛盾,所以{z1,z2,…,zn-1}是Knap(n-1,MaxWeight- w[n])的最优解。

  (2)  正法与1)类似。

 

    2.1-3.  0-1背包问题的递推式

    dp[j] = max{dp[j], dp[j-w[i]]+v[i]}, 其中1=<i<=n, w[i]<=j<=weight. dp[j]表示背包中重量为j时的最大价值


    2.1-4. 伪代码

int Knap(int n){int i,j;for(i=0; i<=Weight; i++)//init dpdp[i] = 0;for(i=1; i<=n; i++){for(j=Weight; j>=w[i]; j--){//if select the ith goods,then dp[j] = v[i]+dp[j-w[i]] dp[j] = max(dp[j], v[i] + dp[j-w[i]]);}}return dp[Weight];//return max value }


   2.2 回文串问题 

    2.2-1.问题描述:

     给出一个字符串,包含大写字母,小写字母和数字,例如Ab3bd,只有插入操作,一次只能添加一个字符,最终的目的是使其成为回文串,即为对称的字符串,上例中的结果可以是Adb3bdA,此时添加2个字符,分别是第二个字符d和最后一个字符A,使其成为回文串.


    2.2-2. 动态规划找出最优子结构:

    si表示给出的字符串的第i个字符,此问题的最优子结构可以描述为:若(si, ......, sj)为插入字符最少获得的回文串,则(s(i+1), ...., s(j-1))也必须为插入最少字符而获得的回文串.

    证明:假设(s(i+1), ...., s(j-1))不是插入最少字符而获得的回文串,那么一定存在另一种插入方式(s'(i+1), ...... , s'(j-1))获得到回文串的插入次数少于(s(i+1), ...., s(j-1)),而此时与(si, ......, sj)为插入字符最少获得的回文串相矛盾,因此该问题具有最优子结构.


    2.2-3.自顶向下的递推公式

     dp[i][j]表示第i个字符到第j个字符时的子字符串(子问题)获得回文串需要的插入次数,

     dp[i][j] = dp[i+1][j-1],  当s[i]=s[j]时

     dp[i][j] = min(dp[i][j-1], dp[i+1][j])+1, 当s[i]!=s[j]时


    2.2-4.伪代码

int DP(char a[],int n){int j;for(int k=2;k<=n;k++){for(int i=1;i<n;i++){j = k+i-1;if(a[i]==a[j])ans[i][j] = ans[i-1][j-1];elseans[i][j] = min(ans[i][j-1],ans[i+1][j])+1;}}       return a[1][n];}


   2.3 编辑距离问题

   2.3-1.问题描述

   指两个字符串(X , Y)之间,字符串X转成字符串Y所需的最少编辑操作次数,记为字符串X到字符串Y的最小编辑距离,记作dp[m][n](其中m,n分别表示字符串X 和字符串Y的长度).允许的编辑操作有替换,插入,删除(此处和算法导论课后的习题不同,减少了3个操作),在这里假设所有操作的代价分别cost(replace), cost(insert),cost(delete).

例如:kityten -> sitting

 1.sityten (k->s)

 2.sitten (删除y)

 3.sittin (e->i)

 4.sitting(插入g)

   2.3-2.最优子结构

  定义dp[i][j]表示表示字符串X的长度为i的子串(X的第1个字符到第i个字符)到字符串Y的长度为j的子串的编辑距离(即最小编辑代价).

分情况讨论:

(1):最后一次操作为替换,若为替换则可知X[i]!=Y[j],因此替换操作后X[i]=Y[j],该问题就转化为X[i-1]转为Y[j-1]的问题,此时dp[i][j]=dp[i-1][j-1]+cost(repace);

最优子结构描述:如果最优一次操作为替换操作,那么dp(i-1,j-1)是字符串X长度为i-1的字串到字符串Y长度为j-1的字串的编辑距离(最小编辑代价)

证明:反证法:若dp(i-1,j-1)不是字符串X长度为i-1的字串到字符串Y长度为j-1的字串的最小编辑代价,那么存在另一种编辑方案,使得dp'(i-1,j-1)小于dp(i-1,j-1).这与前提矛盾,因此具有最优子结构.

(2):最后一次操作为插入操作,则字符串X没有发生改变,字符串Y添加一个字符(第j个字符)因此该问题就转化为X[i]转为Y[j-1]的问题,此时dp[i][j] = dp[i][j-1]+cost(insert)

最优子结构及证明类似替换.

(3):若最后一次操作为删除操作,则字符串X将X[i]删除,对字符串Y没有影响,此时该问题转化为X[i-1]到Y[j]的问题,于是dp[i][j]=dp[i-1][j]+cost(delete)

最优子结构及证明类似替换.


  2.3-3.递推公式

   dp[i][j] = min(dp[i-1][j-1]+cost(replace), dp[i][j-1]+cost(insert), dp[i-1][j]+cost(delete) ).


   2.3-4.伪代码:

int EditDistance(char *X, char *Y){m = strlen(X), n = strlen(Y);int **dp = new int[m][n];for(i=0; i<=m; i++){dp[i][0] = i;}for(i=0; i<=n; i++){dp[0][j] = j;}for(i=1; i<=m; i++){for(j=1; j<=n; j++){if(X[i] == Y[j]){dp[i][j] = dp[i-1][j-1] + cost(replace);}else{dp[i][j] = min(dp[i][j-1]+cost(insert), dp[i-1][j]+cost(delete));}}}return dp[m][n];}



原创粉丝点击