动态规划

动态规划简介

动态规划算法与分治法类似,其基本思想也是将待求解问题分解成若干个子问题,先求解子问题,然后从这些子问题的解得到原问题的解。与分治法不同的是,适合于动态规划算法求解的问题,经分解的得到的子问题往往不是相互独立的。分治算法两个自问题一般是不重叠的
  若用分治法来解这类问题,则分解的得到的子问题数目太多,以至于最后解决原问题需要耗费指数时间。在用分治法求解时,有些子问题被重复计算了许多次。如果我们能够保存已解决子问题的答案,而在需要时再找出已求得的答案,这样就可以避免大量的重复计算。

自顶向下计算会产生重复计算的问题。比如要计算下图中要计算a[i,j]为塔顶的子塔所能获得的最大路径和。那么每走一层都要问下一层它走过最长的路径是啥,红框部分重叠就是为重复部分,那么这部分就被至少问了2次以上。


动态规划应用

1. 计算最长公共子序列长度
  计算最长公共子序列长度的动态规划算法LCSLength以序列X={x1,x2,...,xm}和Y={y1,y2,...,yn}作为输入,计算得到数组c,其中c[i][j]存储序列Xi={x1,x2,...,xi}和序列Yj={y1,y2,...,yj}的最长公共子序列长度。

解题思路
  设A="a[0]...a[m]",B="b[0]...b[n]",并且Z="z[0]...z[k]"为它们最长公共子序列。在找A、B公共子序列时,若a[m]=b[n],则进一步解决一个子问题:找"a[0]...a[m-1]"和"b[0]...b[n-1]"的最长公共子序列。若a[m]≠b[n],则要解决两个子问题:找"a[0]...a[m-1]"和"b[0]...b[n]"的最长公共子序列和找"a[0]...a[m]"和"b[0]...b[n-1]"的最长公共子序列,再取两者中较长者作为A和B的最长公共子序列。
  但LCSLength存在子问题重叠性质,如在计算X和Y的LCSLength时,可能要计算X和Y[n-1]及X[m-1]和Y的LCSLength,而这两个子问题都包含了一个公共子问题,即计算X[m-1]和Y[n-1]的最长公共子序列。所以用c[i][j]记录Xi和Yj的最长公共子序列的长度。当i=0或j=0时,空序列时Xi和Yj的最长公共子序列。故此时c[i][j]=0.其他情况下,由最优子结构性质可建立递归关系如下:

代码

void LSCLength(int m,int n,char *x,char *y,int **c){
   int i,j;
   for(i=1;i<=m;i++) c[i][0]=0;
   for(i=1;i<=n;i++) c[0][i]=0;
   for(i=1;i<=m;i++)          //自底向上计算
       for(j=1;j<=n;i++)
       {  
           if(x[i]==y[j]) { c[i][j] = c[i-1][j-1]+1; }
           else if(c[i-1][j] >= c[i][j-1])  c[i][j]=c[i-1][j]
           else { c[i][j]=c[i][j-1]; }
       }
}

时间复杂度
  由于每个数组单元的计算耗时为O(1)时间,算法LCSLength耗时O(mn)。

2. 0-1背包问题
  0-1背包问题:给定n种物品和一背包。物品i的重量是wi,其价值为vi,背包的容量为C。问应该如何选择装入背包中的物品,使得装入背包中的物品的总价值最大?
  选择装入背包的物品时,对每种物品i只有两种选择,即装入背包或不装入背包。不能将物品i装入背包多次,也不能只装入部分的物品i。因此,该问题成为0-1背包问题。

最优子结构性质
  此问题的形式化描述是:给定C>0,wi>0,vi>0,1<=i<=n,要求找出一个n元0-1向量(x1,x2,...,xn),xi∈{0,1},1<=i<=n,使得∑(i=1n)wixi<=C,而且∑(i=1n)vixi达到最大。


  设(y1,y2,…,yn)是 (3.4.1)的一个最优解.则(y2,…,yn)是下面相应子问题的一个最优解:

证明:使用反证法。若不然,设(z2,z3,…,zn)是上述子问题的一个最优解,而(y2,y3,…,yn)不是它的最优解。显然有
           ∑vizi > ∑viyi (i=2,…,n)
  且        w1y1+ ∑wizi<= c
  因此    v1y1+ ∑vizi (i=2,…,n) > ∑ viyi, (i=1,…,n)
说明(y1,z2, z3,…,zn)是(3.4.1)0-1背包问题的一个更优解——>导出(y1,y2,…,yn)不是背包问题的最优解(因为大前提已经是(y1,y2,…,yn)是 (3.4.1)的一个最优解,而现在又导出一个(y1,z2, z3,…,zn)是(3.4.1)的最优解,所以是前后矛盾的),矛盾。

解题思路
  有编号分别为a,b,c,d,e的五件物品,它们的重量分别是2,2,6,5,4,它们的价值分别是6,3,5,4,6,现在给你个承重为10的背包,如何让背包里装入的物品具有最大的价值总和?


首先要明确这张表是至底向上,从左到右生成的。为了叙述方便,用e2单元格表示e行2列的单元格,这个单元格的意义是用来表示只有物品e时,有个承重为2的背包,那么这个背包的最大价值是0,因为e物品的重量是4,背包装不了。对于d2单元格,表示只有物品e,d时,承重为2的背包,所能装入的最大价值,仍然是0,因为物品e,d都不是这个背包能装的。
  同理,c2=0,b2=3,a2=6。对于承重为8的背包,a8=15,是怎么得出的呢?根据01背包的状态转换方程,需要考察两个值,一个是f[i-1,j],对于这个例子来说就是b8的值9,另一个是f[i-1,j-Wi]+Pi。
  在这里, f[i-1,j]表示我有一个承重为8的背包,当只有物品b,c,d,e四件可选时,这个背包能装入的最大价值。f[i-1,j-Wi]表示我有一个承重为6的背包(等于当前背包承重减去物品a的重量),当只有物品b,c,d,e四件可选时,这个背包能装入的最大价值。f[i-1,j-Wi]就是指单元格b6,值为9,Pi指的是a物品的价值,即6。由于f[i-1,j-Wi]+Pi = 9 + 6 = 15 大于f[i-1,j] = 9,所以物品a应该放入承重为8的背包。
  设所给0-1背包问题的子问题的最优值为m(i,j),即m(i,j)是背包容量为j,可选择物品物品为i,i+1,...,n时0-1背包问题的最优值。有0-1背包问题的最优子结构性质,可以建立计算m(i,j)的递归式如下:

在max{m(i+1,j),m(i+1,j-wi)+vi}中,m(i+1,j)代表不选当前物品,而m(i+1,j-wi)+vi代表选择当前物品(加上当前物品价值)。如果当前物品重量比背包总容量还打的话,那就只能是m(i+1,j)。

代码
  当wi(1<=i<=n)为正整数时,用二维数组m[][]来存储m(i,j)的相应值,可设计解0-1背包问题的动态规划算法 Knapsack如下:

//n为物品个数,c为背包容量
void Knapsack(Type v, int w , int c , int n , Type **m)
{
   int jMax = min(w[n]-1,c);  //比较背包容量和当前物品的重量

   /**先初始化下标最大(n)的**/
   //初始化背包容量从0到jMax的最优值
   for(int j=0; j<=jMax; j++)  m[n][j] = 0; 
   //初始化背包容量>=当前物品重量时m[n][j]的值(若背包容量<当前物品则保留为0)
   for(int j=w[n]; j<=c; j++)  m[n][j] = v[n];

   /**计算下标比n小的**/
   for(int i = n-1; i>1 ; i--){
     jMax = min(w[i]-1,c);
     //没选之前,m[i][j]皆为前面选的价值总和
     for(int j=0; j<=jMax; j++)  m[i][j] = m[i+1][j];
     //j从放得下本物品开始的容量开始
     for(int j=w[i]; j<=c; j++)  m[i][j] = max(m[i+1][j],m[i+1][j-w[i]]+v[i]);
   }
   m[1][c] = m[2][c];
   if(c>=w[1]) m[1][c] = max(m[1][c],m[2][c-w[1]]+v[1]);
}

//构造相应的最优解
void Traceback(Type **m, int w , int c, int n, int x)
{
  for(int i=1;i<=n;i++)
    if(m[i][c]==m[i+1][c]) x[i] = 0;
    else { x[i] = 1;c-=w[i]; }
  x[n] = (m[n][c])?1:0;
}

算法Knapsack计算后,m[1][c]给出所要求的0-1背包问题的最优值。相应的最优解可由算法Traceback计算如下。如果m[1][c]=m[2][c],则x1=0,否则x1=1。当x1=0时,由m[2][c]继续构造最优解。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,132评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,802评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,566评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,858评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,867评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,695评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,064评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,705评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,915评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,677评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,796评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,432评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,041评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,992评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,223评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,185评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,535评论 2 343

推荐阅读更多精彩内容