如何从动态规划的角度去看问题

算法导论(MIT 6.006 第19讲)

动态规划的核心处理流程是什么?

1: 定义子问题

计算子问题的数量

2:猜测(尝试所有可能的方式,获取最好的)

计算选择的数量

3: 关联所有的子问题

计算单个子问题所需要处理的时间

4: 重用子问题结果并记下新的结果,或者使用DP的bottom-up方式(需要注意没有环)

计算总耗时

5: 解决原有的问题

对结果进行组合等等会划掉部分额外的时间

总的来说就是:尝试所有可能的子问题的结果,将最好的可能子结果存储下来,然后重复利用已经解决的子问题,递归去解决所有的问题(猜测+记忆+递归)

它消耗的时间为: 子问题的数量 * 每个子问题处理所需要时间

例1:斐波那契数列

使用递归的方式求斐波那契数列

fib(n):
    if n<=2:f=1;
    else: f= fib(n-1)+fib(n-2);
    return f;

这种方式的运行时间为 θ θ ( 2n/2 2 n / 2 ),空间为O(1)

T(n)=T(n-1)+T(n-2)+O(1) 2T(n-2)= θ θ ( 2n/2 2 n / 2 )

Memoized DP 算法求斐波那契数列

fib(n):
    memo={}
    if n in memo:return memo[n];
    if n<=2:f=1;
    else f=fib(n-1)+fib(n-2);
    memo[n]=f;
    return f;

这种方式的运行时间为 θ(n) θ ( n ) ,空间为O(n)

memo的存在使得实际产生调用的只有 fib(1) …. fib(n),共n次,区域的直接从memo中获取,使用常量的时间

Bottom-up DP算法求斐波那契数列

fib(n):
    fib={}
    for i in range(1,n+1):
        if i<=2: f=1
        else: f=fib[i-1]+fib[i-2]
        fib[i]=f
    return fib[n]

这种方式的运行时间为 θ(n) θ ( n ) ,空间可以只用O(1)

它可以看做是一种拓扑排序(针对DAG),对于使用空间其实只需要记住前两个即可
这里写图片描述

例2:最短路径

要求s到t的最短路径,那么必定会经过与t相邻的一条边,如图示的u,那么最短路径 δ(s,t) δ ( s , t ) = min(u,t)E(δ(s,u)+w(u,t)) m i n ( u , t ) ∈ E ( δ ( s , u ) + w ( u , t ) )

δ(s,u) δ ( s , u ) 就是需要递归调用处理的部分

对于DAG: δ(s,t) δ ( s , t ) 每个子问题的处理时间为 indegree(t)+O(1)

indegree(t):入度数也就是类似(u,t)边的数量,需要去遍历所有t的入边

O(1):判断是不是有入边

总共的执行时间为

vV(indegree(v)+O(1))=O(E+V) ∑ v ∈ V ( i n d e g r e e ( v ) + O ( 1 ) ) = O ( E + V )

这里写图片描述

当图中有环的时候求最短路径产生的问题

要求s到v的最短路径 δ(s,v) δ ( s , v ) ,首选需要去求 δ(s,a) δ ( s , a ) ,然后是 δ(s,b) δ ( s , b ) ,到b节点有两条路径: δ(s,s) δ ( s , s ) δ(s,v) δ ( s , v ) ,此时去memo中查 δ(s,v) δ ( s , v ) 是不存在的,又会这回查询,导致了一个死循环
这里写图片描述

解决图中有环的时候求最短路径的问题

方式是去环,将原来的图一层一层的展开。
假设从s到v需要的路径为k步,那么可以得到 δk(s,v) δ k ( s , v ) = min(b,v)E(δk1(s,b)+w(b,v)) m i n ( b , v ) ∈ E ( δ k − 1 ( s , b ) + w ( b , v ) ) ,当k递减到0的时候,其实也就是从s到s本身
这里写图片描述
所需要的展开层数为:|V|-1

对于求最短路径来讲,最长不能超过|V|-1,否则就是成环,会造成循环的情况(从0开始的计数),这就是为什么Bellman-Ford的外层循环是 |V|-1

每层的节点数为所有的节点。那么总共的节点数为|V’|=|V|(|V|-1)+1=O( V2 V 2 ),边数是|E’|=|E|(|V|-2)+1=O(VE)。转换后的图是DAG图,那么实际上的时间为O(V’+E’)=O(VE)。这也就是从动态规划的角度去看Bellman-Ford算法

节点的数目是1个源点,边的数目是每多一层实际上就多了加了一遍所有的边。

斐波那契数列与最短路径使用动态规划处理步骤的对比

例子斐波那契数列最短路径
1:定义子问题 Fk F k 其中 1kn 1 ≤ k ≤ n δk(s,v) δ k ( s , v ) 其中 vV,0k<V v ∈ V , 0 ≤ k < ‖ V ‖
子问题数量n V2 V 2
2:猜测什么都没做,完全是定义节点v的入边(如果存在的话)
选择的数量1v的入边数+1
3:关联所有的子问题 Fk=Fk1+Fk2 F k = F k − 1 + F k − 2 δk(s,v)=min(u,v)E(δk1(s,u)+w(u,v)) δ k ( s , v ) = m i n ( u , v ) ∈ E ( δ k − 1 ( s , u ) + w ( u , v ) )
子问题的时间 θ(1) θ ( 1 ) θ(1+(v)) θ ( 1 + 入 度 ( v ) )
4:重用子问题结果并记下新的结果for k=1,..,nfor k=0,1,..,|V|-1
总共耗时 θ(n) θ ( n ) θ(VE)+θ(V2) θ ( V E ) + θ ( V 2 ) (如果存在入度,就有后项)
5:解决原有的问题 Fn F n δv1(s,v),vV δ ‖ v ‖ − 1 ( s , v ) , v ∈ V
额外耗时 θ(1) θ ( 1 ) θ(V) θ ( V ) (Bellman-Ford最后的遍历)
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值