要证明的是贪心选择性质 和 最优子结构性质
证明步骤
1. 问题形式化描述
- 理解问题:首先,你需要完全理解问题的要求和限制条件。
- 定义贪心策略:确定一个贪心策略,即在每一步选择中,你将如何做出局部最优的选择。
2. 证明贪心选择性质
- 局部最优导致全局最优:证明如果我们总是选择当前看起来最优的选择,那么最终得到的解也是全局最优的。不会出现这个选择不出现在最终答案的情况。
- 数学归纳法:经常使用数学归纳法来证明贪心选择性质,即证明每一步的贪心选择能够导致问题的一个更小实例的最优解。
3. 证明最优子结构性质
- 子问题的存在:证明原问题的最优解包含子问题的最优解。
- 构建子问题:展示如何从原问题的最优解中提取出子问题的最优解。
eg1 活动安排问题
贪心选择性质
需证明:如果我们总是选择结束时间最早的活动,那么这个选择至少会出现在某个全局最优解中。
感性理解:这样可以尽可能早地释放会议室,为后续的活动提供空间。
证明:假设已经有一个最优的活动集合A,其中包含了活动a1, a2, …, ak,并且这些活动的结束时间是按非递减顺序排列的。如果我们将第一个活动(结束时间最早的活动)替换为活动1(所有活动中结束时间最早的活动),这个新集合A’仍然会是一个相容的活动集合,并且不会比原来的集合A差。因此,我们可以得出结论,结束时间最早的活动至少会被包含在某个最优解中。
最优子结构性质
需证明:如果我们已经安排了结束时间最早的活动,那么剩下的问题(即安排剩下的活动)仍然是一个活动安排问题,其最优解也将是原问题的一个最优解。
证明:假设我们有一个最优的活动集合A,并且我们已经安排了结束时间最早的活动1。如果我们从集合P中删除活动1以及与其冲突的活动,我们得到一个新的活动子集P’。在P’中,我们可以安排的活动集合就是A中除了活动1之外的其他活动。这个子集也是P’的最优活动集合,因为如果我们在P’中安排更多的活动,那么加上活动1后,安排的活动数就会超过A,这与A是最优活动集合的假设相矛盾。
eg2 哈夫曼编码问题
贪心选择性质:
- 对于任何最优编码树,每个叶子节点都有兄弟节点。 如果某个叶子节点没有兄弟节点,那么将其取代其父节点可以得到一个更优的编码树。
- 对于任何最优编码树,频率最小的两个字符 (a_1) 和 (a_2) 必须作为兄弟节点结合在一起,并位于树的最底层。 如果不是这样,通过将它们调整到最下层可以达成更优的编码树。
最优子结构性质:
-
基础情况:对于 (n=1, 2, 3) 的情况,可以直接验证哈夫曼树是最优的。
-
归纳假设:假设对于 (n = k-1) 个字符,哈夫曼树的最优性成立。
-
归纳步骤:考虑 (n = k) 个字符的情况。假设存在一棵最优编码树 (A),但它不遵循哈夫曼树的原则(即不总是合并最小的两个节点)。根据引理,(a_1) 和 (a_2) 必须位于树的最下层。
- 构建虚拟节点:将 (a_1) 和 (a_2) 合并成一个虚拟的叶子节点 (v),得到树 (A’)。树 (A’) 的总编码长度等于树 (A) 的总编码长度减去 (a_1 + a_2)(原因:两兄弟合并到了父节点上。假设根到该父节点的距离是e,原来是a1(e+1)+a2(e+1),合并后是(a1+a2)e,整体少了到a1和a2两个叶子的长度=1的边)
- 哈夫曼树的对应:在哈夫曼树 (H) 中,也存在一个虚拟节点 (v),它是 (a_1) 和 (a_2) 合并的结果。构建树 (H’),其总编码长度等于 (H) 的总编码长度减去 (a_1 + a_2)。
-
比较和矛盾:
- 如果树 (A) 优于哈夫曼树 (H),那么树 (A’) 也应优于树 (H’)。
- 但是,对于节点 (v)、(a_3) 到 (a_k),根据 (n = k-1) 的归纳假设,(H’) 是最优的。这导致矛盾,因为我们假设 (A’) 优于 (H’),但实际上 (H’) 是最优的。
注:
- A’和H’指的是深度n=k-1时的树,A和H是n=k的树
- 最优编码不一定是按照哈夫曼算法得到的,但它只能做到和哈夫曼一样优,不能超过哈夫曼。
eg3 单源最短路-dij算法
贪心策略:在Dijkstra算法的每一步中,从集合 ( V-S ) 中选择具有最短特殊路径长度 ( dist[u] ) 的顶点 ( u ),将其加入到集合 ( S ) 中
贪心选择性质
需证明:对于任意已经在集合S中的顶点u,从源点v开始、经过图中任意顶点到达u的全局最短路径的长度d(v, u)等于从v开始、只经过集合S中顶点到达u的最短路径长度dist(u)。
反证法:
假设存在另一条v到u的最短路径,该路径上某些节点x不在S 中,且该路径长度 d(v,u)<dist[u]
为方便表述,假设从v到u的全局最短路径上,经过的第1个不属于S的顶点为x
(上图)由“v到u的最短路径经过x”,意思就是红线总长比绿线短。
(上图)那么去掉一段,红线就更短了。
如此一来产生矛盾:若红线比绿线更短,那么x应该是比u先加入到集合S中,怎么还会在集合之外呢?
所以,最初假设不成立。
最优子结构性质
需证明:添加u到S中后,重新计算全部顶点的dist,均不增加。
全部顶点分为三类:
- 集合S中的顶点(除u)
- 集合外的顶点i
- 新加入S的u
S中全部顶点的dist都不会再变了,这是贪心选择性。故只需讨论集合外的顶点i。
(下图)红线长度是旧dist[i]值,算法是:如果绿线长度小于红线,那么更新dist[i]。所以dist值只会变小不会变大。
eg4 prim算法
从任意一个顶点开始,逐步扩展最小生成树,直到包含所有顶点。
贪心选择:
- 我们从一个顶点开始,比如叫它a。我们找到与a相连的边中权重最小的边,这条边连接了a和另一个顶点b,我们把这条边叫做y。
- 我们证明这条边y是所有最小生成树中的一条边。假设我们已经有了一个最小生成树G,如果y已经在G中,那我们就没什么好说的,Prim算法显然是正确的。
- 如果y不在G中,我们把y加进去,这时候会形成一个环。在这个环中,除了y以外的边都是连接在最小生成树中的,所以我们可以从环中移除任何一条与a相连的边c。这样我们得到了一个新的生成树G’。
- 因为y是我们能找到的连接a和b的最小边,所以它的权重肯定小于或等于边c的权重。这意味着G’的总权重不会超过G的总权重,所以G’也是一个最小生成树。这样,我们就证明了Prim算法每次选择的边都是正确的。
最优子结构:
- 我们假设Prim算法得到的最小生成树是G,我们找到G中权重最小的两条边,它们连接了两个顶点a和b,我们把这两条边中的一条叫做y。
- 我们把a和b合并成一个新顶点c,删除边y,得到一个新的树G’。如果G’是最小生成树,那么原问题的正确性就得到了证明。
- 如果G’不是最小生成树,那么一定存在另一个最小生成树G’‘,它的总权重小于G’。但是,因为我们在合并a和b时,只考虑了连接它们的最小边,所以任何比G’更小的生成树都必须包含边y。
- 这意味着,如果我们把G’‘中的顶点c重新拆分成a和b,并且把边y加回去,我们得到的新树G’‘‘的总权重仍然小于G。但这与G是最小生成树的事实矛盾,因为G’’'至少和G一样好,但又包含了更多的边。
- 因此,我们的假设不成立,G’必须是最小生成树,这证明了Prim算法的正确性。
eg5 Kruskal算法
按边的权重从小到大排序,然后逐个考虑这些边,如果加入这条边不会形成环,就把它加入最小生成树。
贪心选择:
- 我们假设Kruskal算法得到的树是G。我们考虑一个最小生成树T,如果G和T是一样的,那么问题的正确性就得到了证明。
- 如果G和T不一样,那么G中至少有一条最小权值的边在T中不在。我们把这条边叫做e。
- 我们把e加到T中,这会形成一个环。在这个环中,我们找到一条不在G中的边f,并且移除它,得到新的树T’。
- 如果f的权重大于e,那么T’的总权重就会小于T,这与T是最小生成树的事实矛盾。
- 如果f的权重小于e,那么在Kruskal算法中,我们会选择f而不是e,因为f会先被考虑。这意味着在T中,会和f形成环的边的权重都不会超过f,所以它们都不会被选入G。
- 因此,我们只能得出f的权重等于e的结论,这意味着T’仍然是最小生成树,而且T’和G有更多相同的边。这样,我们就证明了Kruskal算法每次选择的边都是正确的。
最优子结构:
- 我们假设Kruskal算法得到的最小生成树是G,我们找到G中权重最小的边e,把它连接的两个顶点a和b合并成一个新的顶点c,删除边e,得到新的树G’。
- 如果G’是最小生成树,那么原问题的正确性就得到了证明。
- 如果G’不是最小生成树,那么一定存在另一个最小生成树G’‘,它的总权重小于G’。
- 但是,因为我们在合并a和b时,只考虑了连接它们的最小边,所以任何比G’更小的生成树都必须包含边e。
- 这意味着,如果我们把G’‘中的顶点c重新拆分成a和b,并且把边e加回去,我们得到的新树G’‘‘的总权重仍然小于G。但这与G是最小生成树的事实矛盾,因为G’’'至少和G一样好,但又包含了更多的边。
- 因此,我们的假设不成立,G’必须是最小生成树,这证明了Kruskal算法的正确性。