003-贪心算法
Posted Java长征记
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了003-贪心算法相关的知识,希望对你有一定的参考价值。
贪心算法
算法概述
贪心算法也叫贪婪算法,是指在对问题求解时,总是做出在当前看来是最好的选择。也就是说,不从整体最优解出发来考虑,它所做出的仅是在某种意义上的局部最优解。(所谓贪心即只看眼前不管未来)
【注意:】
贪心方法是一种分级处理方法,首先要根据题意,选取一种量度标准(贪心策略),然后根据量度标准对成本进行排序,再根据题目的约束条件进行选择。对于一个问题可能会有不同的量度标准,选择其中可以获得最优解的量度标准(贪心策略)是贪心法设计的核心问题
贪心算法的基本思想
建立数学模型来描述问题。
把求解的问题分成若干个子问题。
对每一子问题求解,得到子问题的局部最优解。
把子问题的解局部最优解合成原来解问题的一个解。
【注意:贪心法要进行正确性证明,证明其不正确举反例即可】
贪心法的正确性证明
【个人感觉应该是贪心法的难点之处】
贪心法正确性证明常用的方法有归纳法(对算法步数归纳、对问题归纳)和交换论证法(从最优解出发,不变坏地替换,得到贪心策略的解)。下面的例子介绍时会详细介绍
活动选择问题——对算法步数归纳
最优装载问题——对问题规模归纳
最小延迟调度——交换论证
贪心算法的性质
贪心选择可以依赖以往所做过的选择,但绝不依赖于将来所作的选择,也不依赖于子问题的解。(区别于动态规划)
贪心算法通常以自顶向下的方式进行,不断迭代做出贪心选择,每做一次贪心选择就将问题简化为规模更小的问题。
【你会发现贪心法其实和动态规划有点相似之处,但也有很大区别】
贪心法的优势
算法简单,时间和空间复杂性低
话不多说直接上栗子,一目了然
经典例子
活动选择问题
最优装载问题
最小延迟调度问题
一一活动选择问题
●问题介绍
S = {1, 2, … , n}为n 项活动的集合, si , fi 分别为活动 i 的开始和结束时间. 活动i与j相容即si ≥fj 或 sj ≥fi .求最大的相容活动集
●贪心策略
策略1:开始时间早的优先排序使 s1≤s2≤…≤sn,从前向后挑选
不正确(反例):
反例:S ={1,2,3 }s1=0, f1=20, s2=2, f2=5, s3=8, f3=15
解(活动1)
策略2:占用时间少的优先排序使得 f1-s1≤ f2-s2≤…≤fn-sn,从前向后挑选
不正确(反例)
反例:S = { 1, 2, 3 }s1=0, f1=8, s2=7, f2=9, s3=8, f3=15
解(活动2)
策略3:结束早的优先排序使 f1≤f2≤…≤ fn,从前向后挑选
解(活动1、3)
●伪码:
●正确性证明(对算法步数归纳)
命题:算法Select执行到第k步,选择k项活动i1=1,i2,…,ik,那么存在最优解 A包含活动 i1=1, i2 ,…, ik .
只要此定理成立,算法至多到第n步得到最优解
归纳证明
设S={1,2,….n}为活动集且f1≤…≤ fn
归纳基础: k=1, 证明存在最优解包含活动 1 ,k=1, 证明存在最优解包含活动 1
证:任取最优解A, A中活动按截止时间递增排列. 如果A的第一个活动为 j,j ≠1, 用1替换A的活动 j 得到解 A',即A' = (A−{ j }) ∪{1}, 由于 f1 ≤ fj , A' 也是最优解,且含有1.
归纳步骤:
假设命题对k为真, 证明对k+1也为真.
证:算法执行到第 k 步, 选择了活动i1=1,i2, …, ik , 根据归纳假设存在最优解 A包 含i1=1,i2,…,ik , A中剩下活动选自集合S'
S' = { i | i∈S, si ≥ fk }
A = { i1, i2, … , ik } ∪ B
证:B是 S'的最优解.(若不然, S' 的最优解为B*, B*的活动比 B多,那么B*∪{1, i2, … , ik } 是 S 的最优解,且比 A的活动多,与 A 的最优性矛盾.)
证:将S' 看成子问题,根据归纳基础,存在 S' 的最优解B' 有S' 中的第一个活动 ik+1, 且 |B' | = |B|, 于是
{ i1, i2, ... , ik } ∪ B'= { i1, i2, ... , ik , ik+1 } ∪ ( B'−{ ik+1})也是原问题的最优解
●程序:
●时间复杂度
O(n) = O(n)(输入时已排好序)
一一最优装载问题
●问题介绍
n 个集装箱1, 2, … , n 要装上轮船,集装箱 i 的重量为 wi , 轮船装载重量限制为C, 无体积限制. 问如何装使得上船的集装箱最多?假设每个箱子的重量 wi≤C.
【类似于0-1背包问题】
●建模
设 <x1, x2, ... , xn> 表示解向量,xi = 0,1(0代表不装,1代表装)xi = 1表示当且仅当第 i 个集装箱被装上船
●算法策略
将集装箱按照重量递增顺序进行排列,轻者优先
●正确性证明(对问题规模归纳)
设集装箱从轻到重记为1, 2, … , n.
命题:对装载问题任何规模为 n 的输入实例,算法得到最优解.
归纳基础:
证:对任何只含 1个箱子的输入实例,贪心法得到最优解. 显然正确.
归纳步骤:
假设对于任何n个箱子的输入实例贪心法都能得到最优解,那么对于任何n+1个箱子的输入实例贪心法也得到最优解.
归纳步骤证明思路
假设对于 n 个集装箱的输入,贪心法都可以得到最优解,考虑 输入N = { 1, 2, … , n+1 } 其中 w1 ≤ w2 ≤ … ≤ wn+1.
证:由归纳假设,对于N' = {2, 3, …, n+1},C' = C− w1,贪心法得到最优解 I'.
令I = I '∪{1}
证:I (算法解)是关于 N 的最优解. 若不然,存在包含 1 的关于 N 的最优解 I*(如果 I* 中没有1,用 1 替换 I* 中的第一个元素得到的解也是最优解), 且 |I*|>| I |;那么I*−{1}是 N' 和C' 的解且| I*− {1}| > | I − {1} | = | I' | 与 I'是关于N' 和C' 的最优解矛盾.
一一最小延迟调度问题
●问题介绍
客户集合A,∀i∈A,ti 为服务时间,di 为要求完成时间,ti, di为正整数. 一个调度f : A→N,f(i)为客户 i 的开始时间. 求最大延迟达到最小的调度,即求 f 使得
●贪心策略
按照 di 从小到大安排,即按完成时间从早到晚安排任务,没有空闲.
●伪码
●正确性证明(交换论证)
没有空闲时间, 没有逆序. 逆序 ( i, j ): f (i) < f (j) 且 di > dj (逆序即i比j结束时间晚却先执行了)
命题:所有没有逆序、没有空闲时间的调度具有相同的最大延迟
证:设 f 没有逆序,在 f 中具有相同完成时间 d 的客户i1, i2, … , ik连续安排, 其开始时刻为 t0, 完成这些任务的时刻是 t,最大延迟为最后任务延迟t−d, 与 i1, i2, … , ik的排列次序无关.t = t0+ (ti1+ ti2+ ...+ tik)
证明思想:从一个没有空闲时间的最优解出发,在不改变最优性的条件下,转变为没有逆序的解。
如果一个最优调度存在逆序,那么存在i < n使得(i,i+1构成一个逆序)
存在逆序(i,j),j = i + 1,那么交换i和j得到的解的逆序数减一,后面证明这个新的调度仍然最优
至多经过n(n-1)/2次交换得到一个没有逆序的最优调度
证:交换相邻逆序仍旧最优
设 f1是一个任意最优解,存在相邻逆序( i, j ) . 交换 i 和 j 的顺序, 得到解 f2. 那么f2的最大延迟不超过 f1的最大延迟.
交换 i, j 与其他客户延迟时间无关
交换后不增加 j 的延迟,但可能增加 i的延迟
i 在 f2的延迟小于 j 在 f1 的延迟 ,因此小于f 1的最大延迟 r
●程序:
●时间复杂度为
O(nlogn) + O(n) = O(nlogn)
【可以看出贪心法的程序设计和思路其实很简单,个人感觉难点就在于证明贪心策略的正确性,如果一个问题可以用贪心法解决的话,首选贪心法】
--End--
谢谢!
以上是关于003-贪心算法的主要内容,如果未能解决你的问题,请参考以下文章