📜  门| GATE-CS-2001 |问题8(1)

📅  最后修改于: 2023-12-03 14:58:24.914000             🧑  作者: Mango

介绍

这是GATE-CS-2001考试中的第8道问题,需要对给定的算法进行分析和实现。该问题主要涉及到图论中的最小生成树和动态规划算法。

问题描述

给定一个连通加权无向图$G=(V,E)$和它的一棵生成树$T=(V',E')$,对于一个权重为$w$的边$e\in E\setminus E'$,我们可以在$T$中删除一条比这条边权重大的边,然后将该边加入$T$中,得到新的生成树$T'=(V',E'\cup{e'}\setminus{e''})$,其中$e''$是$T'\cap {e}$中权重最大的边。现在,我们需要设计一个算法,对于所有满足$w(e)>w(e'')$的边$e\in E\setminus E'$,找到权重最小的$e'$并输出。

解答

这道问题可以用下面的动态规划算法解决:

  • 首先,对于任意一个$e\in E\setminus E'$,我们找到$T\cup {e}$中权重最大的边$e''$,如果$w(e)\le w(e'')$,那么$e$不可能是答案。
  • 然后,我们将$E\setminus E'$中所有满足$w(e)>w(e'')$的边按照权重从小到大排序,设为$e_1,e_2,\cdots,e_k$。
  • 令$S_i$为$e_1,\cdots,e_i$生成的所有树中的最小值,用Kruskal算法来求解。$S_k$就是最终的答案。

接下来,我们来证明这个算法的正确性。

首先,令$T'_i=(V',E'\cup{e_1,\cdots,e_i}\setminus{e''_1,\cdots,e''_i})$,其中$e''_i$是$T'_i\cap {e_1,\cdots,e_i}$中权重最大的边。那么,由定义可知,$S_i$就是$T'_i$中所有边权最小的边权值。

然后,我们通过归纳证明来证明算法的正确性:

  • 当$i=1$时,我们有$S_1=w(e_1)$。因为$w(e_1)>w(e''_1)$,所以$e_1$可以替换$e''_1$得到一颗比$T$更小的生成树,因此$S_1$是正确的。

  • 假设当$i<k$时,$S_i$是正确的。我们需要证明$S_k$也是正确的。即证明,$S_k$可以由$S_{k-1}$通过一次替换得到,并且$w(e_k)=S_k$。

  • 首先,由假设可知,$S_{k-1}$可以由$T'_{k-1}$中满足条件的边生成,而$T'k$可以由$T'{k-1}$替换其中一条边生成。因此,$S_k$可以由$T'_k$中满足条件的边生成,即$S_k\le w(e_k)$。

  • 然后,我们需要证明$S_k\ge w(e_k)$。假设存在一颗树$T_0$,它满足:

    1. 它是由$V'$和至少一条来自$E\setminus E'$中满足条件的边生成的。
    2. 它的边权之和小于$w(e_1)+\cdots+w(e_i)$。
    3. $w(e_1)+\cdots+w(e_i)$是$T_0$中所有边中最小的。

    那么,由假设可知,$S_{i}=w(T_{i-1}+{e_i})$。因为$T_0$是由$V'$和来自$E\setminus E'$中至少一条满足条件的边生成的,所以我们可以得到$T_0$中至少有一条边$e_j$满足$w(e_j)>w(e''_j)$。

    然后,我们将$T_0$中权重最大的边删除,加入$e_i$,得到新的树$T_1$。这里需要注意,由于$T_0$中存在一条边满足$w(e_j)>w(e''_j)$,因此$e''_j$不可能是$e_i$中的边。因此,$T_1$是由$V'$和至少一条来自$E\setminus E'$中满足条件的边生成的。

    由于$e_i$是$E\setminus E'$中权重最小的边,因此$w(e_i)\le w(e_j)$。因此,我们可以用$e_j$替换$e_i$,得到一颗树$T_2$,满足$T_2$是由$V'$和至少一条来自$E\setminus E'$中满足条件的边生成的,其边权之和小于等于$w(e_1)+\cdots+w(e_i)$。因此,$S_{i}=w(T_{i-1}+{e_i})\ge w(T_2)\ge S_{i-1}$。

    综上,我们得到$S_k=S_{k-1}\cup{e_k}\setminus{e''k}$。因此,$S_k$可以由$S{k-1}$通过一次替换得到,并且$S_k\le w(e_k)$。由于归纳假设,$S_{k-1}$是正确的,因此$S_k$也是正确的。

综上,我们证明了该算法的正确性。下面给出该算法的实现代码:

# S1: 构造 T'
T_prime = {}
for e in E - E_prime:
    e_max = max([e_ for e_ in T+{e} if e_ != e], key=lambda e: e.weight)
    if e.weight > e_max.weight:
        T_prime[e] = e_max
   
# S2: 排序所有符合条件的边
e_list = sorted([e for e in E - E_prime if e.weight > T_prime[e].weight], key=lambda e: e.weight)

# S3: Kruskal 算法求解 S_k
S_k = set(T_prime.values())
for e in e_list:
    S_k.add(e)
    S_k.discard(max([e_ for e_ in S_k if e_ != e], key=lambda e_: e_.weight))