Skip to content

最小樹形圖

在一張有向圖中,如果存在一點 root,滿足:

  • 從 root 可以到圖上其他節點。
  • root 的入度為 0,其他節點入度為 1。

該圖稱為樹形圖。

在一張圖裡,邊權重和最小的子圖稱為最小樹形圖,也可以說是有向圖版的最小生成樹。

演算法步驟如下:

  • 除了 root 之外的所有點選擇權重最小的入邊,如果有點沒有入邊,代表無法形成樹形圖。
  • 判斷被選進的邊中有沒有環,如果有環將該還縮成一個點,
  • 如果沒出現環終止演算法,否則回到步驟一。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
struct Edge
{
    int u, v, cost;
};
// in,pre: 每個點最小權重入邊的權重和頂點
// id: 點的新編號, vis: 被誰訪問
vector<int> in(MXN), pre(MXN), id(MXN), vis(MXN);
vector<Edge> E;

int dmst(int root, int n, int m)
{
    int res = 0;
    while (1)
    {
        // 尋找非 root 的所有點權重最小的入邊
        fill(in.begin(), in.end(), INF);
        for (Edge &edge : E)
            if (edge.u != edge.v && edge.cost < in[edge.v])
            {
                in[edge.v] = edge.cost;
                pre[edge.v] = edge.u;
            }
        // 如果有一個點(除了 root)沒入邊,就無法形成樹形圖
        for (int i = 0; i < n; ++i)
            if (i != root && in[i] == INF)
                return -1;
        // 找環和縮點
        int num = 0;
        fill(id.begin(), id.end(), -1);
        fill(vis.begin(), vis.end(), -1);
        in[root] = 0;
        for (int i = 0; i < n; ++i)
        {
            res += in[i];
            int v = i;
            while (vis[v] != i && id[v] == -1 && v != root)
            {
                vis[v] = i;
                v = pre[v];
            }
            if (v != root && id[v] == -1)
            {
                for (int j = pre[v]; j != v; j = pre[j])
                    id[j] = num;
                id[v] = num++;
            }
        }
        // 沒有環代表已形成樹形圖
        if (num == 0)
            break;
        // 將沒有形成環的點和所有邊重新編號
        for (int i = 0; i < n; ++i)
            if (id[i] == -1)
                id[i] = num++;
        for (int i = 0; i < (int)E.size(); ++i)
        {
            Edge &edge = E[i];
            // 假設點 v 有兩條入邊 E1,E2,第一次選擇 E1,在縮點後 E2
            // 被選擇,所增加的 cost = E2 的權重 - E1
            // 的權重,為了避免重複計算權重,將邊重新編號時把所有未選入的邊權重扣除。
            if (id[edge.u] != id[edge.v])
                edge.cost -= in[edge.v];
            edge.u = id[edge.u];
            edge.v = id[edge.v];
        }
        n = num;
        root = id[root];
    }
    return res;
}

例題練習

UVa 11183 - Teen Girl Squad

給定一張圖,求出最小生成樹。

UVa 11865 - Stream My Contest

給定有 個點和 條邊的網路,每條邊有起點、終點和寬頻,訊號會從點 傳送到其他地方,在建造成本最大為 的情況下,最多可達到多少寬頻速度?

二分搜寬頻速度 代表只使用寬頻 的邊,建造成本是否