动态规划详解

1                                                            1,1

3            2                                         2,1                    2,2

4           10           1                        3,1                  3,2                    3,3

4              3            2             20         4,1          4,2                     4,3                 4,4

(a)数字三角形                                                                 (b)状态编号

 

从第一行的数开始,每一次可以向下或者向右走一格,一直走到最下行,把沿途的数全部加起来,如何使这个和最大?

如果熟悉回溯法,会发现这是一个动态决策问题,每次有两种选择,左下或者右下.用回溯法求出所有可能的路线,就可以从中选择最优路线,但是有路线2的n次方条,效率不可忍受

可以用抽象的方法来思考问题,把当前位置看成一个状态,用(i,j)位置来表示.然后定义状态(i,j)的指标函数,d(i,j)为从格子(i,j)出发出发能获得的最大值

看不同的状态如何转移,往左走,则走到(i+1,j),往右走,则走(i+1,j+1),自由选择,选择其中较大的一个

状态转移方程                d(i,j)=a(i,j)+max{d(i+1,j),d(i+1,j+1)}

如果从(i+1,j)出发得到部分和是最大的,则加上a(i,j)之后也是最大的 .这个性质叫做最优子结构

动态规划的核心是状态和状态转移方程.

实现计算

1.递归计算

int d(int i,int j)

{

     return a[i][j]+(i==n? 0:d(i+1,j)>d(i+1,j+1));

}

效率太低,递归时调用关系树造成了重复计算

2.递推计算

int i,j;

for(j=1;j<=n;j++)

     d[n][j]=a[n][j];

for(i=n-1;i>=1;i—)

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

      d[i][j]=a[i][j]+d[i+1][j]>?d[i+1][j+1];

递推的时间复杂度为   状态总数x每个状态的决策个数x决策时间

3.记忆化搜索

int d(int i,int j)

{

      if(d[i][j]>=0)

        return d[i][j];

      return d[i][j]=a[i][j]+(i==n?0:d(i+1,j)>?d(i+1,j+1));

}

把计算结果保存在d[i][j]中,每次记录是否计算过

也可以用另一个数组vis[]保存是否被计算过

0-1背包问题

n种物品,每种有无穷多个,第i种体积为vi,重量是wi,选一些到容量为c的背包中,使得背包内物体在总体积不超过c的前提下重量尽量大

刚才的方法已经不适用了,原来的状态转移太混乱了.要消除这种混乱,要让决策有序化.这就是多段决策问题

每次做一次决策就可以得到解的一部分.所有的决策做完就得到最终解.回溯法中每个决策对应一颗子树,每个解答书对应一个解.节点层数是下一个待填充位置cur,就是即将完成的决策序号,在动规中称为阶段

多段决策可用动态规划解决.d(i,j)表示第i层,背包剩余容量为j的最大重量和.

d(i,j)=max{d(i+1,j),d(i+1,j-V[i])+w[i]}

边界是i>n时d(i,j)=0,j<0时为负无穷

白话一点,d(i,j)表示把第i,i+1,i+2,,,n个物品装到容量为j的背包中的最大重量和,事实上,这个说法常用阶段和层这样的术语,

代码(结果是d[1][c])

for(int i=n;i>=1;i—)

    for(int j=0;j<=c;j++)

    {

           d[i][j]=(i==n? 0:d[i+1][j]);

           if(j>=v[i])

               d[i][j]>?=d[i+1][j-v[i]]+w[i];

    }

i必须逆序枚举,但j的循环次序是无关紧要的

还有一种对称的状态定义,用f(i,j)表示把前i个物品装到容量为j的背包中的最大重量和

状态转移方程:  f(i,j)=max{f(i-1,j),f(i-1,j-v[i])+w[i]}

最终答案为g(n,C)

for(int i=1;i<=n;i++)

    for(int j=0;j<=C;j++)

    {

          f[i][j]=(i==1? 0:f[i-1][j]);

         if(j>=v[i])

             f[i][j]>?=f[i-1][j-v[i]]+w[i];

    }

动态规划的类型很多,今天就介绍最优子结构和多段决策,还有树形动规,集合动规,DAG动规日后为一一研究介绍


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值