算法导论—分治法思想、动态规划思想、贪心思想

40 篇文章 6 订阅

分治法的思想

将原问题分解为几个规模较小但类似于原问题的子问题,递归地求解这些子问题,然后再合并这些子问题的解来建立原问题的解。
分治模式在每层递归时都有三个步骤:
分解原问题为若干子问题,这些子问题是原问题的规模较小的实例。
解决这些子问题,递归地求解各子问题。然而,若子问题的规模足够小,则直接求解。
合并这些子问题的解成原问题的解。

归并排序算法完全遵循分治模式。直观上其操作如下:
分解:分解待排序的n个元素的序列成各具n/2个元素的两个子序列
解决:使用归并排序递归地排序两个子序列
合并:合并两个已排序的子序列以产生已排序的答案

  • 求解递归式
    1. 代入法求解
    2. 递归树方法
    3. 主方法

动态规划

每次决策依赖于当前状态,又随即引起状态的转移。一个决策序列就是在变化的状态中产生的,所以,这种多阶段最优化决策解决问题的过程就称为动态规划。
基本思想与分治法类似,也是将待求解的问题分解为若干个子问题(阶段),按顺序求解子阶段,前一问题的解,为后一问题的求解提供了有用的信息在求解任意子问题时,列出各种可能的局部解,通过决策保留那些有可能达到最优的局部解,丢弃其他局部解。依次解决各子问题,最后一个子问题就是初始问题的解。
由于动态规划解决的问题多数有重叠子问题这个特点,为减少重复计算,对每个子问题只解一次,将其不同阶段的不同状态保存在一个二维数组中。
与分治法最大的区别是:适用于用动态规划法求解的问题,经分解后得到的子问题往往不是互相独立的(即下一个子阶段的求解是建立在上一个子阶段的解的基础上)
动态规划的关键点:
1、最优化原理,也就是有最优子结构性质。这指的是一个最优化策略具有这样的性质,无论过去状态和决策如何,对前面的决策所形成的状态而言,余下的决策必须构成最优策略,简单来说就是一个最优化策略的子策略总是最优的,如果一个问题满足最优化原理,就称其具有最优子结构性质
2、无后效性,指的是某个状态下的决策的收益,只与状态和决策相关,与达到该状态的方式无关
3、子问题的重叠性,动态规划将原来指数级的暴力搜索算法改进到了具有多项式时间复杂度的算法,其中的关键在于解决了冗余、重复计算的问题
基本步骤:

  1. 刻画一个最优解的结构特征,如果一个问题的最优解包含其子问题的最优解,我们就称此问题具有最优子结构性质。
  2. 递归地定义最优解的值
  3. 计算最优解的值,通常采用自底向上的方法
  4. 利用计算出的信息构造一个最优解

什么问题适合动态规划

如何判断一个问题是不是DP问题呢?适合DP求解的最优化问题通常具有以下两个特征:

最优子结构

如果一个问题的最优解包含其子问题的最优解,我们就称此问题具有最优子结构性质。

以0-1背包问题(给你一个可装载重量为W的背包和N个物品,每个物品有重量和价值两个属性。其中第i个物品的重量为wt[i],价值为val[i],现在让你用这个背包装物品,最多能装的价值是多少?)为例说明什么是子问题:假设你已经选择了第i个物品,那么问题就变为”如何在可装载重量为W-wt[i]和剩下的N-1个物品的情况下求最多能装的价值“,这就是原问题的一个子问题。

我们可以通过以下步骤来判断一个问题是否具有最优子结构性质:

​ 1. 先做出一个选择。做出这次选择会产生一个或多个待解的子问题

​ 2. 假定第1步的选择是最优解的一个选择,确定第1步的选择会产生哪些子问题

​ 3. 判断以下结论是否成立:作为构成原问题的最优解的组成部分,每个子问题的解就是它本身的最优解。证明这个结论的最好办法是反证法,还是以上述的0-1背包问题为例:如果存在一个选择方案使得子问题”在可装载重量为W-wt[i]和剩下的N-1个物品的情况下求最多能装的价值“的解大于原来的解,那么用这个选择方案替换原来的选择方案,就存在一个整体选择方案使得原问题”在可装载重量为W和N个物品的情况下求最多能装的价值“解大于最优解,这就与最优解的定义矛盾了,因此结论成立

重叠子问题

如果算法反复求解相同的子问题,就称最优化问题具有重叠子问题性质

多项式与伪多项式时间

多项式时间算法 (polynomial time algorithm) 表示:算法的复杂度与输入的规模呈多项式关系。
伪多项式时间算法 (pseudopolynomial time algorithm) 表示:算法的复杂度与输入规模呈指数关系,与输入的数值大小呈多项式关系。
举两个对比的例子:
冒泡排序:给定 n 个64位的数字,进行 n-1 次扫描交换,将数字从小到大排序。
素数测试:给定数字 n,通过从 2 到根号 n 的整数遍历,判断 n 是否为素数。字面上看,两者复杂度都是 O ( n k ) O(n^k) O(nk)( k 为整数) 。但区别在于,前者的 n 是数字个数的多少,后者的 n 是数字的大小。因此,前者输入总规模 s1 增长与数字大小无关, s 1 = 64 n s1 = 64n s1=64n;后者增长规模与数字大小紧密相关,输入总规模为 s 2 = log ⁡ n s2 = \log n s2=logn 。所以可知冒泡排序中复杂度 O ( n 2 ) = O ( s 1 2 / 6 4 2 ) O(n^2) = O(s1^2/64^2) O(n2)=O(s12/642) 为多项式算法,后者素数测试 O ( n ) = O ( 2 s 2 ) O(n) = O(2^{s_2}) O(n)=O(2s2)为伪多项式算法

0-1背包问题是伪多项式时间复杂度,对于具有 n n n个项目且尺寸为 W W W的背包的无界背包问题,运行时间为 O ( N W ) O(NW) O(NW) W W W在输入长度上不是多项式, n n n的确是输入规模的一部分,输入了 n n n个重量与价值,但是 W W W并不是输入规模,对于一个数 W W W,需要 m = log ⁡ W m=\log W m=logW的位数来表示.因此, m m m才是输入规模的一部分。所以 O ( n ∗ W ) = O ( n ⋅ 2 m ) O(n*W)=O(n·2^m) O(nW)=O(n2m),所以是NPC问题,这就是伪多项式的原因。

贪心算法:

贪心算法求解问题的条件:

  1. 贪心选择性质:我们可以通过做出局部最优选择来构造全局最优解
  2. 最优子结构:一个问题的最优解包含其子问题的最优解

设计贪心算法的步骤:

  1. 将最优化问题转换形式:对其做出一次选择后,只剩下一个子问题需要求解
  2. 证明做出贪心选择后,原问题总是存在最优解,即贪心选择总是安全的
  3. 证明做出贪心选择后,剩余的子问题满足性质:其最优解与贪心选择组合即可得到原问题的最优解,这样就得到了最优子结构
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

之墨_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值