跟我一起学算法——贪心算法

@


贪心算法(Greedy Algorithms)

思想

每次选择总是选出当前最优的解
特点 :只计算部分子问题,而动态规划需要计算全部子问题,但同样依赖最优子结构性质。
步骤 :先排个序,再按每次最优循环选择。

条件

  1. 最优子结构性质
    问题的整体最优解中包含子问题的最优解
  2. 贪心选择性质:局部最优=>全局最优
    本次贪心选择与剩余子问题的最优解可以构成原问题的最优解。

贪心算法正确性的证明()

包括两部分:贪心选择性质的证明和最优子结构证明

贪心选择的证明,方法一:

  1. 给出贪心算法A的描述
  2. 反证:先给出一个贪心算的最优解A,然后假设非贪心选择的B是最优解,B可由A序列中调换元素
    得到,证明B的解值比A差。

贪心选择的证明,方法二——剪枝法:

  1. 贪心算法A的描述
  2. 假设O是一个最优算法
  3. 找出O和A中的一个不同
  4. Exchange这个不同的东西,然后讨论得到的算法O'不比O差
  5. 若这种不同共有n个,然后我exchange n次就可以消除所有不同,得到A,且算法的质量不比O差。
    这说明A as good as O,所以A是最优的。如huffman树贪心选择性质证明

最优子结构证明:

  1. 给出最优解A和子问题最优解B
  2. 假设B不是最优,C才是最优
  3. 证明将C替换B后的得到的A'优于A,矛盾。

应用:活动选择问题

也可以看做时间片段集合问题。给一些时间片段集合T={(a1,b1)(a2,b2)...(an,bn)},
找出一个元素个数最多的子集S,子集中的每个元素的时间片段没有交叉。O(n)

先把活动按结束时间排序,然后每次找上一次活动结束后可以开始的第一个活动。

贪心选择的证明

https://blog.csdn.net/rookie_ly/article/details/79039295

应用:Huffman编码

算法

使用最优前缀码,每次挑选二个频度值最小的字符,作为孩子节点,父结点的频度值为孩子的频度和。
haffman树的叶节点是满的。

def Huffman(charList):
    while len(charList)!=1:
        d=Node()
        x=min(charList)
        d.left=x
        charList.del(x)
        y=min(charList)
        d.right=y
        charList.del(y)
        d.val=d.left.val+d.right.val
        charList.append(d)
    return charList[0] #根结点

时间复杂度O(nlgn)

证明贪心选择性质

令C为一字符集,C中每个字符c∈C的频度为f(c),x,y∈C是二个具有最小频度值的字符,则必定
存在C的一种最优前缀码,使x,y的码长相同且仅最后一位不同。

  1. 最优前缀码树是一棵叶结点满二叉树
  2. 令T是最优前缀码树,证明x,y码长相同且仅最后一位不同

证明最优子结构性质

若在C中去除字符x,y,加上频度为f[z]=f[x]+f[y]的新字符z,则构成一新的字符集
C’=C-{x,y}∪{z}。令T’是C’的一棵最优前缀码树,那么当用孩子结点为x,y的内部结点替换C’
中的叶结点z后得到的新树T是一棵最优前缀码树。

反证法

用huffman编码斐波那契数

a:1 11111110
b:1 1111110
c:2 111110
d:3 11110
e:5 1110
f:8 110
g:13 10
h:21 0
可推广到n。

应用:最小生成树( Kruskal Algorithm for Minimum Spanning Tree)

对于边集合E,先按每个边的cost排序,从小到大,然后按如下方法构造一个MST,每次选cost最小
的边加入MST,如果这条边使MST出现环路,则舍弃,然后重复上面的操作。

贪心算法正确性证明

  1. 图为G(V,E),假设上述算法A的MST为T2,有 最优算法O的MST为T1,T2 != T1.即至少存在一条边e,
    它在T1,不在T2。e把T1分为两个集合Ta和Tb,且Ta中的点+Tb中的点=V。则在T2中一定存在一条边w(w!=e),
    w的一个v在Ta中,另一个在Tb中。由A的定义知,选了w而没选e,说明 cost(w)<cost(e)
  2. 构造一棵新的树T3= E(T1) - e + w,显然T3是一个生成树,且cost(T3)<=cost(T1),则T3是MST。
  3. T3比T1更接近T2,若T1和T2有n条不同的边,则按上述方法经过n次变化,可以得到T2,同时cost<=T1,
    则T2是MST。

删数问题

贪心策略

从高到低位搜索,若各位数字递增,则删除最后一个数字,窦泽删除第一个递减区间的首字符(换句
话说:删除递增区间的最后一个数字),然后回到首位继续搜索。

贪心选择正确性证明

T1 = a_1*10^(n-1) + a_2*10^(n-2)+ ...+a_(n-1)*10+a_n

  1. 若各位数单调递增,则删除最后一个数:
    T2 = a_1*10^(n-2) + a_2*10^(n-3)+ ...+a_(n-2)*10+a_(n-1)
    若删除的另一个数a_k,则
    T2'=a_1*10(n-2)+a_2*10(n-3)+a_(k-1)*10(n-k)+a_(k+1)*10(n-k-1)+...+
    a_(n-1)*10+a_n
    显然,T2-T2'=(a_k-a_(k+1)*10^(n-k-1) + (a_(k+1)-a_(k+2))*10^(n-q-2)+...+
    (a_(n-2)-a_(n-1))*10+(a_(n-1)-a_n) <= 0
    所以贪心选择是正确的。
  2. 若存在递减,则删除递减序列第一个数:

其他应用

部分背包问题
找硬币问题
prime算法和迪杰斯特拉算法

胚(matroids)

定义

胚是满足以下条件的有序对M=(S,I):

  1. S有穷非空集
  2. I的遗传性:I是S的非空独立子集族,使得若B∈I,且A是B的子集,则A∈I。即B独立,B的子集亦独立。
  3. M的交换性:若A∈I,B∈I且|A|<|B|,则存在一个元素x∈B-A,使得A∪{x} ∈I。
    例如,无向图G=(V,E),SG=E为G中的边集,IG为SG的无回路边集族,MG=(SG,IG)是一个胚。

最大独立子集

给定一个胚M=(S,I),对于I中一个独立子集A∈I,若S中有一个元素x不属于A,使得将x加入A后仍保
持A的独立性,即A∪{x}∈I,则称x为A的一个可扩张元素(extension)。
当胚中的一个独立子集A没有可扩张元素时,称A是一个最大独立子集。
胚中所有最大独立子集大小相等。

加权胚和最优子集

权值最大的独立子集称为最优子集。
通用贪心算法:按权值递减排序,循环取权值最大的+独立性检测。O(nlgn)

posted @ 2020-03-03 15:18  chzhyang  阅读(910)  评论(0编辑  收藏  举报