贪心算法和拟阵
动态规划能够很好的帮助我们解决很多的最优解问题,但是对于许多的最优解问题,使用动态规划进行求解会显得问题过于复杂,造成复杂度过高。贪心算法和动态规划一样是用于求解最优解问题的方法,它期望通过所做的局部最优解选择来产生一个全局最优解。
活动选择问题
有一系列相互竞争的活动,他们都需要占用同一资源,这意味着在任意时刻都不能够出现两个活动在同时进行,那么如何进行调度,能够在一段时间内进行的活动数目最大呢?
在这里,我们定义活动集合为A=a1,a2,...,anA={a_1,a_2,...,a_n}A=a1,a2,...,an,他们的开始时间和结束时间定义为sis_isi和fif_ifi,那么活动不冲突就是他们的活动区间[si,fi)[s_i,f_i)[si,fi)和[sj,fj)[s_j,f_j)[sj,fj)不重合。我们先按照活动结束时间进行排序:
i | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 |
---|---|---|---|---|---|---|---|---|---|---|---|
sis_isi | 1 | 3 | 0 | 5 | 3 | 5 | 6 | 8 | 8 | 2 | 12 |
fif_ifi | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
我们先用动态规划的方式来求解。我们将按照结束时间单调递增的相互不冲突的活动集合定义为SijS_{ij}Sij,其中第一个活动的开始时间不早于fi−1f_{i-1}fi−1,最后一个活动的结束时间不晚于sj+1s_{j+1}sj+1,也就是说SijS_{ij}Sij中的每一个活动的开始时间都大于前一个活动的结束时间,对于一个活动集合AijA_{ij}Aij,我们都可以求出其中相互兼容的活动集合SijS_{ij}Sij。
在AijA_{ij}Aij中,我们可以发现,对于某个活动aka_kak,存在Aij=Aik∪ak∪AkjA_{ij}=A_{ik}\cup a_{k}\cup A_{kj}Aij=Aik∪ak∪Akj,同样的,对于AijA_{ij}Aij中相互兼容的活动SijS_{ij}Sij,也存在Sij=Sik∪ak∪SkjS_{ij}=S_{ik}\cup a_{k}\cup S_{kj}Sij=Sik∪ak∪Skj,那么,我们就可以根据集合中每个aka_kak,将AijA_{ij}Aij拆分成两个子集合,再分别求出子集合的最大值进行合并,比较每个不同的aka_kak拆分的结果,并取最大值即可。在这里定义AijA_{ij}Aij中最大的相互兼容的活动集合的个数为cijc_{ij}cij,可以得到递归式:
cij={0,Aij为空maxi<k<j(cijk+ckj+1)Aij不为空c_{ij} =
\begin{cases}
0, & A_{ij}为空 \\
\max_{i<k<j}({c_{ijk+c_{kj}+1}}) & A_{ij}不为空 \\
\end{cases}cij={0,maxi<k<j(cijk+ckj+1)Aij为空Aij不为空
我们再用贪心算法来解一下这个问题。在这里,我们进行一系列的选择,每一次都选择能够和之前的选择相兼容的,并且结束时间最早的活动,我们认为这样能够给接下来的选择更大的空,最后得到一个最优解为a1,a4,a8,a11{a_1,a_4,a_8,a_11}a1,a4,a8,a11,其伪代码如下:
S=ϕS = \phiS=ϕ
将A中的活动按照结束时间从小到大排列
for x in S
if x的开始时间大于S最后一个元素的结束时间
S=S∪{x}S = S\cup \{ x\}S=S∪{x}
return S
为什么可以进行这样的选择呢?我们定义ama_mam为AijA_{ij}Aij中结束时间最早的活动,那么ama_mam将会在AijA_{ij}Aij的某一个最大兼容活动子集中被使用,证明如下:
设SijS_{ij}Sij为AijA_{ij}Aij的一个最大兼容活动子集,aka_kak为SijS_{ij}Sij的第一个活动,若ak≠ama_k\ne a_mak=am,那么我们构造子集Sij′=(Sij−{ak}∪{am})S'_{ij}=(S_{ij}-\{ a_k\} \cup \{ a_m\} )Sij′=(Sij−{ak}∪{am})。其中,我们很容易得出fm<=dkf_m <= d_kfm<=dk,所以ama_mam和SijS_{ij}Sij中除了aka_kak以外的其他活动都是相互兼容的,即sij′s'_{ij}sij′也是AijA_{ij}Aij的一个最大兼容活动子集,即ama_mam在AijA_{ij}Aij的的某一个最大兼容活动子集中被使用。
在活动选择问题中,我们既可以用动态规划解,也可以用贪心策略解,但是我们也很容易比较得出这两种方法中贪心的复杂度更小。
贪心策略和动态规划的比较
贪心和动态规划都利用的最优子结构的性质,但是动态规划是自底向上的方法,其对每一个问题都是先对子问题求解再做选择,从而得到最优解;而贪心算法则不同,他是自顶向下的方法,对于每一个问题,他都是先做出选择,在对子问题求最优解。
0-1背包问题:有nnn个物品,对于第iii个物品,它的价格为viv_ivi,重量为wiw_iwi,每个物品都是不可分割的完整物品,而有一个背包可以容纳总重为WWW,问背包能够装下的最大价格为多少。
部分背包问题:再0-1背包中,每一个物品都可以拿取部分,而不是只能整个整个的拿。
这两个背包问题我们都可以使用动态规划来求解,而对于部分背包问题,我们也可以使用贪心策略来求解:优先选择性价比vi/wiv_i/ w_ivi/wi的物品。而对于0-1背包问题却不能使用贪心策略来求解,因为按照贪心策略做出选择后会再背包下留下无法利用的空间而在成浪费,所以应对某个物品放进去和不放进去的结果进行比较。
拟阵
对于很多的贪心问题,都可以使用拟阵来解决。
拟阵为一个序对M=(S,l)M=(S,l)M=(S,l),其满足以下条件:
- SSS为一个又穷的非空集合。
- lll为SSS的一个非空子集族,成为SSS的独立子集,其具有遗传性:若存在B∈lB\in lB∈l且A⊆BA\subseteq BA⊆B, 则A∈lA \in lA∈l。
- 拟阵具有交换性:如果A∈l,b∈lA\in l, b\in lA∈l,b∈l,且∣A∣<∣B∣|A|<|B|∣A∣<∣B∣,则存在x∈B−Ax\in B-Ax∈B−A,使得A∪{x}∈lA \cup \{ x\} \in lA∪{x}∈l。
在这里,我们用有向图来举例子:图G=(V,E)G=(V,E)G=(V,E)存在拟阵MG=(SG,lG)M_G=(S_G,l_G)MG=(SG,lG),其中,SG=ES_G=ESG=E,即为G的边集,lGl_GlG为EEE中使得其构成森林的子集形成的子集族。
我们可以很快得出,因为森林的子集依然是森林,所以其具有遗传性。对于交换性,其证明如下:
我们设两个森林GAG_AGA和GBG_BGB,且∣GA∣<∣GB∣|G_A|<|G_B|∣GA∣<∣GB∣,所以可以得出GAG_AGA中的树多于GBG_BGB中的树,那么就存在GBG_BGB中的某棵树的某一条边,它两个顶点属于GAG_AGA中的两棵不同的树,即将两棵GAG_AGA中的树连接成了一棵树(而没有形成环),从而可以证明其交换性质。自此,可以得出每一个无向图都存在对应的拟阵。
拟阵的最大独立子集
我们成拟阵M=(S,l)M=(S,l)M=(S,l)中,对于独立子集A∈lA\in lA∈l,存在AAA之外的元素xxx使得A∪{x}∈lA\cup \{ x\} \in lA∪{x}∈l,则成xxx为AAA的一个扩张。
对于独立子集A∈lA\in lA∈l,若其不存在任何扩张,则称其为最大独立子集。一个拟阵的最大独立子集的大小都是相同的。
加权拟阵
我们将拟阵的元素xxx加上权值w(x)w(x)w(x),将独立子集的权值定义为它包含的所有元素的权值之和,即w(A)=∑x∈Aw(x)w(A)=\sum_{x \in A} w(x)w(A)=∑x∈Aw(x)。这样,我们就可以很容易的使用贪心策略来求拟阵的最大独立子集,其伪代码如下:
A=ϕA = \phiA=ϕ
将S中的元素按照权值从大到小排列
for x in S
if A∪{x}∈lA\cup \{ x\} \in lA∪{x}∈l
A=A∪{x}A = A\cup \{ x\}A=A∪{x}
return A
而对于其他各种贪心问题,我们都可以将其转化为求拟阵的最大独立子集,下面以求带权无向连通图的最小生成树为例:
设无向连通图的各个边的权值为w(e)w(e)w(e),将其转换为拟阵之后,拟阵的元素与图的边一一对应。我们设拟阵的元素的权值为w′(e)=w0(e)−w(e)w'(e)=w_0(e)-w(e)w′(e)=w0(e)−w(e),其中w0(e)w_0(e)w0(e)大于图所有边的权值。这样,拟阵权值最大的元素就对应着图权值最小的边,这样求得拟阵的最大独立子集就与图的最小生成树相对应了。