題目: UVa - 11504 - Dominos

題目說明

給一些骨牌的相對關係,求最少需要推倒幾個骨牌才能使得所有骨牌都倒下。

Input: 第一行為一整數 T,表示總共有 T 組測資,每組測資的第一行為兩個整數 nm,代表有 n 個骨牌 ( 序號為 1 ~ n ),及 m 個相對關係,後面 m 行每行會有兩個整數 uv,表示若 u 倒下則 v 也會倒下。

Output: 輸出最少需要推倒幾個骨牌才能使得所有骨牌都倒下。

解題思路

其實就是找 SCC ( Strongly Connected Component ),SCC 即在有向圖上任意選兩點 uvu 能夠走到 vv 也能夠走到 u。因此我們在 SCC 中任意推倒一個骨牌就能使得整個 SCC 都倒下,之後把每個 SCC 都當成一個點 ( 即縮點的概念 ),之後找出每個 SCC 被幾個其他的 SCC 所接入,如果 SCC 有被其他的 SCC 接入,表示只要推倒那個 SCC,這個 SCC 也會被推倒,最後看有幾個 SCC 沒有被接入即是答案。

參考解法

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
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
#include <iostream>
#include <algorithm>
#include <unordered_set>
#include <vector>
#include <stack>

using namespace std;

/*
reference:
https://ppt.cc/fMdaHx
https://ppt.cc/fnAeqx
*/

static auto __ = []
{
ios_base::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
return 0;
}();

int TIME;
int cnt; // 計算目前有幾個 SCC
vector<int> dfn;
vector<int> low;
vector<int> com; // 紀錄點屬於哪個 SCC
vector<vector<int>> G;
unordered_set<int> inStack;
stack<int> s;

void dfs(int u)
{
dfn[u] = low[u] = ++TIME;
s.push(u);
inStack.insert(u);

for (auto& v : G[u])
{
if (dfn[v])
{
if (inStack.count(v)) low[u] = min(dfn[v], low[u]);
continue;
}

dfs(v);
low[u] = min(low[v], low[u]);
}

if (dfn[u] == low[u])
{
int tmp;
do
{
tmp = s.top();
s.pop();
com[tmp] = cnt;
inStack.erase(tmp);
} while (tmp != u);
++cnt;
}
}

int main()
{
int T;
cin >> T;

while (T--)
{
int n, m;
cin >> n >> m;

// init
TIME = 0;
cnt = 0;
dfn.assign(n + 1, 0);
low.assign(n + 1, 0);
com.assign(n + 1, 0);
G.assign(n + 1, vector<int>());
inStack.clear();
while (!s.empty()) s.pop();

while (m--)
{
int u, v;
cin >> u >> v;

G[u].push_back(v);
}

// 找出 SCC ( Strongly Connected Component )
for (int u = 1; u <= n; ++u) if (!dfn[u]) dfs(u);

// 計算 SCC 被幾個其他的 SCC 接入
vector<int> degree(cnt);
for (int u = 1; u <= n; ++u) for (auto& v : G[u])
{
if (com[u] != com[v]) ++degree[com[v]];
}

int ret = 0;
// 如果 SCC 沒有被其他的 SCC 接入則需要一次來推倒
for (auto& de : degree) if (!de) ++ret;
cout << ret << '\n';
}
}

參考資料

UVa 11504 - Dominos | 天邊。世界
UVa 11504 - Dominos | NaiveRed’s Blog