算法复习_贪心算法

贪心算法

能够用贪心算法解决的问题,也展现两大性质:

  • 最优子结构
  • 贪心选择性质

最优子结构性质与dp类似,不再赘述。

贪心选择性质:当我们去做决定时间,要确保是目前最好的决定。通过选择局部最优解以期望得到全局最优解。

这就需要我们证明当要做选择时,最优解的其中一个是贪心选择,因此,选择贪心解才是安全的。除了贪心选择之外,其他子问题都是空的。

活动选择问题

输入n个活动的集合S a 1 , a 2 , … , a n a_1,a_2,\dots,a_n a1,a2,,an,以及每个活动的开始时间和结束时间 s i , f i s_i,f_i si,fi。输出最大数量的可选活动的集合。

当两个活动的时间间隔不重叠时则这两个活动是相容的。

假设活动被按照结束时间进行排序,假设一个最优解包括了活动 a k a_k ak,这产生了两个子问题。

  • a 1 , a 2 , … , a k − 1 a_1,a_2,\dots,a_{k-1} a1,a2,,ak1中选择彼此兼容的活动,并且结束时间要比 a k a_k ak的开始时间早;
  • a k + 1 , … , a n a_{k+1},\dots,a_n ak+1,,an中选择彼此兼容的活动,并且开始时间要比 a k a_k ak的结束时间晚。

我们同样可以利用”剪切-粘贴“法证明两个子问题的解同样是最优的。

s i j s_{ij} sij为S中活动的子集,这些活动开始的时间比 a i a_i ai结束的时间晚,并且结束的时间比 a j a_j aj开始的时间早。我们的目的就是在 s i j s_{ij} sij中选出最大数量的互相相容的活动。

让c[i,j]为 s i j s_{ij} sij中互相相容活动的最大数量。

dp解法
c [ i , j ] = { 0 s i j = ϕ m a x { c [ i , k ] + c [ k , j ] + 1 } s i j ! = ϕ c[i,j] = \left\{ \begin{matrix} 0 \quad s_{ij} = \phi \\ max\{c[i,k] + c[k,j] + 1 \} \quad s_{ij} !=\phi \end{matrix} \right. c[i,j]={0sij=ϕmax{c[i,k]+c[k,j]+1}sij!=ϕ
但是在活动选择问题中,具有贪心选择性质。

定理:如果S是按照结束时间排序的活动选择问题,那么S中存在最优集合A使得第一个结束的活动在A中。

证明:假设am是集合S中结束最早的活动,A为S的一个最大兼容子集。aj是A中结束最早的活动。如果am==aj则已经证明。如果不相等,则可以构造一个新的最大兼容子集A’,将A中的aj替换成am,已知A中活动相互兼容,而fj<fm。所以A‘中的活动也相互兼容。并且|A|=|A’|。因此我们成功构造了一个包含最早结束活动的最大兼容子集A’,得证。

因此,我们可以递归的使用贪心选择法解决此问题,并且除了贪心选择的子问题之外其他子问题都是空的。以此问题为例,因为s1<f1且f1是最早结束的活动,所以不会有活动结束的时间早于s1。因此,所有与a1兼容的活动都是在a1结束之后才开始的。

Recursive_Activity_Selector(s,f,i,j)
m <- i+1
while m<j and s[m] < f[i]
	do m <- m+1 //s[m] >= f[i]
if m < j
	then return {am} and Recursive_Activity_Selectors(s,f,m,j)
else return none

时间复杂度 Θ ( m ) \Theta(m) Θ(m)

迭代贪心算法:

GREEDY_ACTIVITY_SELECTOR(s,f)
n<- length(s)
A<- {a1}
i<-1
for m <- 2 to n
	if s[m] >= f[i]
		A <- A and {am}
		i <- m
return A

贪心算法的典型步骤:

  • 将最优化问题转换为这样的形式:对其做出一次选择之后,只剩下一个子问题待解决。
  • 证明做出贪心选择之后,原问题总是存在最优解,即贪心选择是安全的。
  • 证明做出选择之后,剩余的子问题满足性质:最优解与贪心组合即可得到问题的最优解

分数背包问题

证明分数背包问题具有贪心选择性质:
https://blog.csdn.net/u010339647/article/details/50588499

找零钱问题

给定零钱的币值,使得其组合等于n并且用的硬币数量最小。

贪心选择:永远选择币值最大的,并不是都正确。

dp:
令c[p]为找零p元所需要的硬币最少数量。x为最优解中使用的第一个硬币的值。

因此c[p] = 1 + c[p-x]。尝试所有可能的x找到最小的。
c [ p ] = { m i n i : d i < p { c [ p − d i ] + 1 } p > 0 0 p = 0 c[p] = \left\{ \begin{matrix} min_{i:d_i<p} \{c[p-d_i] + 1\} \quad p > 0 \\ 0 \quad p =0 \end{matrix} \right. c[p]={mini:di<p{c[pdi]+1}p>00p=0

Dp_change(n)
c[0] = 0
for p = 2 to n
	min = maxn
	for i=1 to k
		if (d[i] <= p)
			if (c[p-d[i]+1<min)
				min = c[p-d[i]] +1
				coin = i
	c[p] = min
	s[p] = coin

时间复杂度 O ( n k ) O(nk) O(nk)

什么时候才可以进行贪心选择?

需要证明:
在这里插入图片描述

哈夫曼编码树

给文本编码需要满足的条件:尽可能使用少的编码长度,满足前缀码性质。

最佳前缀码

一种前缀编码的平均长度是所有符号的长度乘以他的频率之和。
A B L ( C ) = ∑ x ∈ S f x ∣ c ( x ) ∣ ABL(C)= \sum_{x\in S} f_x |c(x)| ABL(C)=xSfxc(x)
我们要做的就是使其最小。

因此采用哈夫曼编码树,它是一棵满二叉树,除了叶节点都有两个子节点。它使用的贪心思想是使得频率越高的节点越靠近根节点。

Huffman(S) 
	if |s|=2
		设置一个根节点连接两个叶节点返回
	else
		选出S中两个频率最小的节点y and z
		s' = s
		将y和z从s'中删去
		在s'中插入新的字母w,使得w的频率等于y和z之和
		给T‘的叶节点w加上孩子节点y和z并赋值给T
		返回T

T(n) = T(n-1) + O(n) (每次去两个点加一个新的点)

时间复杂度 O ( n 2 ) O(n^2) O(n2)

如何找到频率最小的两个节点: 单调队列,时间复杂度 O ( n l o g n ) O(nlogn) O(nlogn)

证明哈夫曼编码的正确性(具有最优子结构和贪心选择性质)

参考算法导论:P248-249 (偷个懒,太长了)

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值