跳到主要内容

京都大学 情報学研究科 知能情報学専攻 2025年8月実施 情報学基礎 F2-2

Author

祭音Myyura

Description

Fig. 1 shows a weighted undirected graph GG. The vertex set of GG is V={a,b,c,d,e,f}V = \{a, b, c, d, e, f\}, and the edge set of GG is E={(a,b),(a,c),(a,e),(b,c),(b,d),(d,e),(d,f),(e,f)}E = \{(a, b), (a, c), (a, e), (b, c), (b, d), (d, e), (d, f), (e, f)\}. For each edge (u,v)E(u, v) \in E, the order of uu and vv is not distinguished. In Algorithms 1 and Algorithm 2, VV and EE are referred to as G.VG.V and G.EG.E, respectively. The weight w(u,v)w(u, v) of each edge in GG is given in Fig. 1. A minimum spanning tree of GG is a connected subgraph that contains all vertices in GG, has no cycles, and minimizes the total weight of its edges.

Figure 1.

Q.1

Answer the following questions.

(1) List all the edges of the minimum spanning tree of GG, and answer the total weight of those edges.

(2) Suppose that a cut (S,VS)(S, V - S) of GG is defined by the vertex set S={d,f}S = \{d, f\}. List all the edges between SS and VSV - S.

(3) Prove by contradiction that the edge (b,d)(b, d) is included in the minimum spanning tree of GG.

Q.2

Prim's algorithm is a greedy method for constructing a minimum spanning tree. The algorithm starts from an arbitrary root rVr \in V and grows the tree by iteratively selecting edges with the smallest weights w(u,v)w(u, v), until the tree includes all vertices in VV. To select the next vertex to add, the algorithm manages the vertices using a min-priority queue QQ. Each vertex vVv \in V in the queue has an attribute v.keyv.key, which stores the weight of the smallest edge weights between vv and the tree. For each step, the vertex with the smallest keykey is extracted from QQ and added to the tree. The parent of a vertex vv in the tree is denoted by v.parentv.parent. Answer the following questions about this algorithm.

(1) Complete Algorithm 1: MST-PRIM, which shows the pseudo-code for Prim's algorithm, by filling in the blanks (a)\fbox{(a)} and (b)\fbox{(b)}. In Algorithm 1, QQ denotes the set of vertices stored in the min-priority queue. The following operations are used:

  • Insert(Q,u)\operatorname{Insert}(Q, u) inserts a vertex uu into QQ.
  • Extract-Min(Q)\operatorname{Extract-Min}(Q) removes and returns the vertex in QQ that has the smallest keykey.
  • Decrease-Key(Q,v,w(u,v))\operatorname{Decrease-Key}(Q, v, w(u, v)) updates the value of v.keyv.key to w(u,v)w(u, v) for vertex vv in QQ. Assume that the priority queue QQ maintains its order so that the vertex with the smallest keykey can always be extracted without searching. This reordering is performed after each of the operations Insert\operatorname{Insert}, Extract-Min\operatorname{Extract-Min}, and Decrease-Key\operatorname{Decrease-Key}.
Algorithm 1: :MST-PRIM(G,w,r)10for each vertex uG.V20u.key=30u.parent=NIL40r.key=050Q=60for each vertex uG.V70INSERT(Q,u)80while Q90u=EXTRACT-MIN(Q)10for each vG.V adjacent to u11if v(a) and w(u,v)<(b)12v.parent=u13v.key=w(u,v)14DECREASE-KEY(Q,v,w(u,v))\begin{array}{l} \hline \textbf{Algorithm 1: } \text{:MST-PRIM}(G, w, r) \\ \hline \mathbf{1}\phantom{0} \quad \textbf{for each } \textit{vertex } u \in G.V \\ \mathbf{2}\phantom{0} \quad \vert \quad u.key = \infty \\ \mathbf{3}\phantom{0} \quad \vert \quad u.parent = \text{NIL} \\ \mathbf{4}\phantom{0} \quad r.key = 0 \\ \mathbf{5}\phantom{0} \quad Q = \emptyset \\ \mathbf{6}\phantom{0} \quad \textbf{for each } \textit{vertex } u \in G.V \\ \mathbf{7}\phantom{0} \quad \vert \quad \text{INSERT}(Q, u) \\ \mathbf{8}\phantom{0} \quad \textbf{while } Q \neq \emptyset \\ \mathbf{9}\phantom{0} \quad \vert \quad u = \text{EXTRACT-MIN}(Q) \\ \mathbf{10} \quad \vert \quad \textbf{for each } v \in G.V \textit{ adjacent to } u \\ \mathbf{11} \quad \vert \quad \vert \quad \textbf{if } v \in \text{\fbox{(a)}} \textit{ and } w(u, v) < \text{\fbox{(b)}} \\ \mathbf{12} \quad \vert \quad \vert \quad \quad v.parent = u \\ \mathbf{13} \quad \vert \quad \vert \quad \quad v.key = w(u, v) \\ \mathbf{14} \quad \vert \quad \vert \quad \quad \text{DECREASE-KEY}(Q, v, w(u, v)) \\ \hline \end{array}

(2) Suppose that Algorithm 1 is applied to the graph GG with r=dr = d. List all the vertices vv remaining in QQ at the end of each iteration of the while loop as pairs (v,v.key)(v, v.key), sorted in ascending order of keykey. In the answer, write each state of QQ on a separate line, in execution order, until Q=Q = \emptyset. If multiple vertices have the same keykey, list them in alphabetical order. The state of QQ just before entering the while loop is as follows (do not include this in your answer):

(d,0),(a,),(b,),(c,),(e,),(f,)(d, 0), (a, \infty), (b, \infty), (c, \infty), (e, \infty), (f, \infty)

(3) Answer the asymptotic upper bound of the worst-case running time when Algorithm 1 is executed on the graph G=(V,E)G = (V, E). Use the Big-O notation and express the answer using V|V| and E|E|, where |\cdot| denotes the number of elements in a set. Assume that the reorganization of the priority queue associated with each of the operations Insert\operatorname{Insert}, Extract-Min\operatorname{Extract-Min}, and Decrease-Key\operatorname{Decrease-Key} takes O(V)O(|V|) time. Answer with the smallest order.

Q.3

Kruskal's algorithm derives a minimum spanning tree by sequentially adopting an edge that does not generate a cycle, in ascending order of edge weight. Algorithm 2 shows a pseudo-code of Kruskal's algorithm in which Union-find algorithm is used to evaluate whether a cycle is generated by the selected edge. Union-find can efficiently check whether two elements belong to the same set by expressing a set with a tree structure. Answer the following questions.

(1) Suppose that Kruskal's algorithm is applied to the graph GG with the weights ww shown in Fig. 1. Show the edges composing the obtained minimum spanning tree in order of being adopted. If there are multiple edges with the same weight, any of them can be selected first.

(2) Let v.parentv.parent denote a parent node of a node vv in a tree structure. Assume v.parent=vv.parent = v when vv is a root. Express the tree TT shown in Fig. 2 in the format of {(vi,vi.parent)},i=1,2,3,4\{(v_i, v_i.parent)\}, i = 1, 2, 3, 4, where the root of TT is v2v_2.

(3) Fill in the blank (c)\fbox{(c)} to complete Algorithm 2.

(4) The computing efficiency can be improved when the line 14 in Algorithm 2 is replaced with v.parent=FindSet(v.parent)v.parent = \operatorname{FindSet}(v.parent). Explain the reason. You may use diagrams.

Algorithm 1: :MST-KRUSKAL(G,w)10Assume that G.E is given with a list structure20MST=30for each vG.V40v.parent=v50Sort G.E into ascending order by weight w60for each (u,v)G.E70if (c)80MST=MST{(u,v)}90Union(u,v)10return MST11FindSet(v)12if vv.parent13return FindSet(v.parent)14return v.parent15Union(u,v)16ru=FindSet(u)17rv=FindSet(v)18if ru==rv19return20ru.parent=rv\begin{array}{l} \hline \textbf{Algorithm 1: } \text{:MST-KRUSKAL}(G, w) \\ \hline \mathbf{1}\phantom{0} \quad \text{Assume that } G.E \text{ is given with a list structure} \\ \mathbf{2}\phantom{0} \quad MST = \emptyset \\ \mathbf{3}\phantom{0} \quad \textbf{for each } v \in G.V \\ \mathbf{4}\phantom{0} \quad \quad v.parent = v \\ \mathbf{5}\phantom{0} \quad \text{Sort } G.E \text{ into ascending order by weight } w \\ \mathbf{6}\phantom{0} \quad \textbf{for each } (u, v) \in G.E \\ \mathbf{7}\phantom{0} \quad \quad \textbf{if } \text{\fbox{(c)}} \\ \mathbf{8}\phantom{0} \quad \quad \quad MST = MST \cup \{(u, v)\} \\ \mathbf{9}\phantom{0} \quad \quad \quad \text{Union}(u, v) \\ \mathbf{10} \quad \textbf{return } MST \\ \mathbf{11} \quad \text{FindSet}(v) \\ \mathbf{12} \quad \quad \textbf{if } v \neq v.parent \\ \mathbf{13} \quad \quad \quad \textbf{return } \text{FindSet}(v.parent) \\ \mathbf{14} \quad \quad \textbf{return } v.parent \\ \mathbf{15} \quad \text{Union}(u, v) \\ \mathbf{16} \quad \quad ru = \text{FindSet}(u) \\ \mathbf{17} \quad \quad rv = \text{FindSet}(v) \\ \mathbf{18} \quad \quad \textbf{if } ru == rv \\ \mathbf{19} \quad \quad \quad \textbf{return} \\ \mathbf{20} \quad \quad ru.parent = rv \end{array}

Figure 2.

题目描述

图 1 给出带权无向图 GG。其顶点集与边集分别为

V={a,b,c,d,e,f},V=\{a,b,c,d,e,f\},
E={(a,b),(a,c),(a,e),(b,c),(b,d),(d,e),(d,f),(e,f)}.E=\{(a,b),(a,c),(a,e),(b,c),(b,d),(d,e),(d,f),(e,f)\}.

对每条边 (u,v)E(u,v)\in E,不区分 u,vu,v 的次序。在算法中,V,EV,E 分别记作 G.V,G.EG.V,G.E;边权 w(u,v)w(u,v) 如图所示。GG 的最小生成树是包含 GG 中全部顶点、连通且无环,并使边权总和最小的子图。

  1. 回答下列问题。

    (1)列出 GG 的最小生成树的所有边,并给出这些边的总权重。

    (2)用顶点集 S={d,f}S=\{d,f\} 定义割 (S,VS)(S,V-S),列出连接 SSVSV-S 的全部边。

    (3)使用反证法证明边 (b,d)(b,d) 一定包含在 GG 的最小生成树中。

  2. Prim 算法是一种构造最小生成树的贪心算法。它从任意根 rVr\in V 开始,每次选择权重最小的适当边扩展树,直至树包含 VV 中全部顶点。算法使用最小优先队列 QQ 管理待加入顶点;队列中每个顶点 vv 的属性 v.keyv.key 保存 vv 与当前树之间最小边的权重。每一步从 QQ 中取出 keykey 最小的顶点并加入树,vv 在树中的父顶点记作 v.parentv.parent

    (1)补全下面 Prim 算法伪代码中的空格 (a)\fbox{(a)}(b)\fbox{(b)}。其中 QQ 表示最小优先队列中的顶点集合,并使用以下操作:

    • Insert(Q,u)\operatorname{Insert}(Q,u):把顶点 uu 插入 QQ
    • Extract-Min(Q)\operatorname{Extract-Min}(Q):从 QQ 删除并返回 keykey 最小的顶点;
    • Decrease-Key(Q,v,w(u,v))\operatorname{Decrease-Key}(Q,v,w(u,v)):把 QQ 中顶点 vvv.keyv.key 更新为 w(u,v)w(u,v)

    假设优先队列始终维持顺序,因而无需搜索即可取出 keykey 最小的顶点;每次执行 Insert\operatorname{Insert}Extract-Min\operatorname{Extract-Min}Decrease-Key\operatorname{Decrease-Key} 后都会重新整理队列。

    算法 1:MST-PRIM(G,w,r)10for each  vertex uG.V20u.key=30u.parent=NIL40r.key=050Q=60for each  vertex uG.V70Insert(Q,u)80while Q90u=Extract-Min(Q)10for each vG.V adjacent to u11if v(a) and w(u,v)<(b)12v.parent=u13v.key=w(u,v)14Decrease-Key(Q,v,w(u,v))\begin{array}{l} \hline \textbf{算法 1:}\operatorname{MST\text{-}PRIM}(G,w,r)\\ \hline \mathbf{1}\phantom{0}\quad \textbf{for each }\textit{ vertex }u\in G.V\\ \mathbf{2}\phantom{0}\quad |\quad u.key=\infty\\ \mathbf{3}\phantom{0}\quad |\quad u.parent=\mathrm{NIL}\\ \mathbf{4}\phantom{0}\quad r.key=0\\ \mathbf{5}\phantom{0}\quad Q=\emptyset\\ \mathbf{6}\phantom{0}\quad \textbf{for each }\textit{ vertex }u\in G.V\\ \mathbf{7}\phantom{0}\quad |\quad \operatorname{Insert}(Q,u)\\ \mathbf{8}\phantom{0}\quad \textbf{while }Q\ne\emptyset\\ \mathbf{9}\phantom{0}\quad |\quad u=\operatorname{Extract-Min}(Q)\\ \mathbf{10}\quad |\quad \textbf{for each }v\in G.V\textit{ adjacent to }u\\ \mathbf{11}\quad |\quad |\quad \textbf{if }v\in\fbox{(a)} \textit{ and }w(u,v)<\fbox{(b)}\\ \mathbf{12}\quad |\quad |\quad |\quad v.parent=u\\ \mathbf{13}\quad |\quad |\quad |\quad v.key=w(u,v)\\ \mathbf{14}\quad |\quad |\quad |\quad \operatorname{Decrease-Key}(Q,v,w(u,v))\\ \hline \end{array}

    (2)把算法 1 应用于图 GG,并令 r=dr=d。在 while 循环每次迭代结束时,把 QQ 中剩余的全部顶点写成 (v,v.key)(v,v.key),按 keykey 升序排列。按执行顺序每行写一个 QQ 的状态,直至 Q=Q=\emptyset;若多个顶点的 keykey 相同,则按字母顺序排列。进入循环之前的状态如下,不要把它写入答案:

    (d,0),(a,),(b,),(c,),(e,),(f,).(d,0),(a,\infty),(b,\infty),(c,\infty),(e,\infty),(f,\infty).

    (3)求算法 1 在一般图 G=(V,E)G=(V,E) 上运行时最坏情形时间复杂度的渐近上界。使用大 OO 记号,以 V,E|V|,|E| 表示答案,并给出可能的最小阶。假设每次 Insert\operatorname{Insert}Extract-Min\operatorname{Extract-Min}Decrease-Key\operatorname{Decrease-Key} 所伴随的优先队列重组均耗时 O(V)O(|V|)

  3. Kruskal 算法按边权升序依次选取不会产生环的边,从而求得最小生成树。算法 2 使用并查集判断所选边是否产生环;并查集通过树结构表示集合,以高效判断两个元素是否属于同一集合。

    (1)对图 1 中的 GG 及其权重执行 Kruskal 算法,按被采用的先后顺序写出所得最小生成树的边。同权边可任选先后。

    (2)设 v.parentv.parent 表示树中顶点 vv 的父结点,根结点满足 v.parent=vv.parent=v。图 2 中树 TT 的根为 v2v_2;用

    {(vi,vi.parent)},i=1,2,3,4\{(v_i,v_i.parent)\},\qquad i=1,2,3,4

    的形式表示该树。

    (3)填写空格 (c)\fbox{(c)},补全算法 2。

    (4)若把算法 2 第 14 行替换为

    v.parent=FindSet(v.parent),v.parent=\operatorname{FindSet}(v.parent),

    则计算效率会提高。说明原因,可以使用示意图。

    算法 2:MST-KRUSKAL(G,w)10假设 G.E 以列表结构给出20MST=30for each vG.V40v.parent=v50按权重 w 将 G.E 升序排序60for each (u,v)G.E70if (c)80MST=MST{(u,v)}90Union(u,v)10return MST11FindSet(v)12if vv.parent13return FindSet(v.parent)14return v.parent15Union(u,v)16ru=FindSet(u)17rv=FindSet(v)18if ru==rv19return20ru.parent=rv\begin{array}{l} \hline \textbf{算法 2:}\operatorname{MST\text{-}KRUSKAL}(G,w)\\ \hline \mathbf{1}\phantom{0}\quad \text{假设 }G.E\text{ 以列表结构给出}\\ \mathbf{2}\phantom{0}\quad MST=\emptyset\\ \mathbf{3}\phantom{0}\quad \textbf{for each }v\in G.V\\ \mathbf{4}\phantom{0}\quad |\quad v.parent=v\\ \mathbf{5}\phantom{0}\quad \text{按权重 }w\text{ 将 }G.E\text{ 升序排序}\\ \mathbf{6}\phantom{0}\quad \textbf{for each }(u,v)\in G.E\\ \mathbf{7}\phantom{0}\quad |\quad \textbf{if }\fbox{(c)}\\ \mathbf{8}\phantom{0}\quad |\quad |\quad MST=MST\cup\{(u,v)\}\\ \mathbf{9}\phantom{0}\quad |\quad |\quad \operatorname{Union}(u,v)\\ \mathbf{10}\quad \textbf{return }MST\\ \mathbf{11}\quad \operatorname{FindSet}(v)\\ \mathbf{12}\quad |\quad \textbf{if }v\ne v.parent\\ \mathbf{13}\quad |\quad |\quad \textbf{return }\operatorname{FindSet}(v.parent)\\ \mathbf{14}\quad |\quad \textbf{return }v.parent\\ \mathbf{15}\quad \operatorname{Union}(u,v)\\ \mathbf{16}\quad |\quad ru=\operatorname{FindSet}(u)\\ \mathbf{17}\quad |\quad rv=\operatorname{FindSet}(v)\\ \mathbf{18}\quad |\quad \textbf{if }ru==rv\\ \mathbf{19}\quad |\quad |\quad \textbf{return}\\ \mathbf{20}\quad |\quad ru.parent=rv\\ \hline \end{array}

考点

  • 最小生成树的割性质:识别跨割的最轻边,并用交换论证或反证法证明边的必选性。
  • Prim 算法:维护顶点键值与父结点,跟踪最小优先队列的状态并分析给定队列实现下的复杂度。
  • Kruskal 算法:按权重排序边,利用并查集检测环并确定选边顺序。
  • 并查集与路径压缩:理解 FindSetUnion 的树表示,以及路径压缩为何降低后续查询成本。

Kai

Q.1

(1)

The edges of the minimum spanning tree are

(a,e), (a,b), (d,f), (a,c), (b,d).\boxed{ (a,e),\ (a,b),\ (d,f),\ (a,c),\ (b,d) }.

Their total weight is

1+2+2+3+3=11.1+2+2+3+3=\boxed{11}.

(2)

For

S={d,f},VS={a,b,c,e},S=\{d,f\}, \qquad V-S=\{a,b,c,e\},

the edges crossing the cut (S,VS)(S,V-S) are

(b,d), (d,e), (e,f).\boxed{ (b,d),\ (d,e),\ (e,f) }.

(3)

Assume, for contradiction, that a minimum spanning tree TT does not contain (b,d)(b,d).

Add (b,d)(b,d) to TT. This creates a cycle. Since bSb\notin S and dSd\in S, the path from bb to dd in TT must contain an edge crossing the cut (S,VS)(S,V-S). Because (b,d)T(b,d)\notin T, that edge must be either (d,e)(d,e) or (e,f)(e,f), whose weights are 55 and 44, respectively.

Removing that edge from the cycle and keeping (b,d)(b,d), whose weight is 33, produces a spanning tree with smaller total weight. This contradicts the minimality of TT.

Therefore,

(b,d) is contained in every minimum spanning tree of G.\boxed{(b,d)\text{ is contained in every minimum spanning tree of }G}.

Q.2

(1)

The blanks are

(a)=Q,(b)=v.key.\boxed{\text{(a)}=Q}, \qquad \boxed{\text{(b)}=v.key}.

Thus the condition is

if vQ and w(u,v)<v.key.\textbf{if }v\in Q\textbf{ and }w(u,v)<v.key.

(2)

Starting from r=dr=d, the states of QQ after successive iterations of the while loop are

(f,2), (b,3), (e,5), (a,), (c,)(f,2),\ (b,3),\ (e,5),\ (a,\infty),\ (c,\infty)
(b,3), (e,4), (a,), (c,)(b,3),\ (e,4),\ (a,\infty),\ (c,\infty)
(a,2), (c,4), (e,4)(a,2),\ (c,4),\ (e,4)
(e,1), (c,3)(e,1),\ (c,3)
(c,3)(c,3)
.\emptyset.

The vertices are extracted in the order

d, f, b, a, e, c.d,\ f,\ b,\ a,\ e,\ c.

(3)

There are V|V| insertions and V|V| extractions. Since each priority-queue reorganization takes O(V)O(|V|) time, these operations take

O(V2).O(|V|^2).

The adjacency lists contain O(E)O(|E|) entries, and Decrease-Key\operatorname{Decrease-Key} is called at most O(E)O(|E|) times. Thus these updates take

O(VE).O(|V||E|).

Hence the total running time is

O(V2+VE).O(|V|^2+|V||E|).

Because the graph is connected, EV1|E|\ge |V|-1, so the smallest equivalent bound is

O(VE).\boxed{O(|V||E|)}.

Q.3

(1)

One possible order in which Kruskal's algorithm adopts the edges is

(a,e), (a,b), (d,f), (a,c), (b,d).\boxed{ (a,e),\ (a,b),\ (d,f),\ (a,c),\ (b,d) }.

Their weights are

1, 2, 2, 3, 3,1,\ 2,\ 2,\ 3,\ 3,

respectively. The order of edges having the same weight may be interchanged.

(2)

Since v2v_2 is the root, the parent representation is

{(v1,v2), (v2,v2), (v3,v2), (v4,v1)}.\boxed{ \{ (v_1,v_2),\ (v_2,v_2),\ (v_3,v_2),\ (v_4,v_1) \} }.

(3)

An edge is adopted only when its endpoints belong to different components. Therefore,

(c)  =  FindSet(u)FindSet(v).\boxed{ \text{(c)}\;=\; \operatorname{FindSet}(u)\ne\operatorname{FindSet}(v) }.

(4)

The improved version of FindSet is

FindSet(v)
if v != v.parent
v.parent = FindSet(v.parent)
return v.parent

The assignment

v.parent=FindSet(v.parent)v.parent=\operatorname{FindSet}(v.parent)

makes every visited vertex point directly to the root. This operation is called path compression.

For example, a path

v1v2v3v4v_1\to v_2\to v_3\to v_4

is changed after FindSet(v1) into

v1v4,v2v4,v3v4.v_1\to v_4,\qquad v_2\to v_4,\qquad v_3\to v_4.

Thus the tree becomes shallower, and later FindSet operations require fewer parent-pointer traversals. Therefore, repeated cycle tests in Kruskal's algorithm become more efficient.