11770 – Lighting Away
コンテンツ
有向グラフが与えられる.何個の頂点を選べば,それらから出る有向道で頂点を被覆できますか.っていう問題.
入力サイズを見たかんじだと,線形ぐらいの計算量ならば良さそう.
で,パッと思いつくのが:
もし,グラフに閉路がない→簡単(入次数が0の点を数える)→強連結成分分解→閉路無し有向グラフ.
ただ,下のコードはシンプルとは言えない気がする.
もっと単純にできると思うが,もうすでに強連結成分分解のコードがあるのでそちらを利用してみた.
//http://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&category=117&page=show_problem&problem=2870 #include <iostream> #include <vector> #include <stack> using namespace std; typedef vector< vector<int> > AdjacencyList; typedef vector<int>::const_iterator vit; void visit(const AdjacencyList &g, int *label, stack<int> &stack, const int &u, int *num, int *low, int &index, int &size) { stack.push(u); low[u] = num[u] = ++index; for(vit v = g[u].begin(); v != g[u].end(); ++v) { if (label[*v] != -1) continue; if (num[*v] == 0) visit(g, label, stack, *v, num, low, index, size); low[u] = min(low[u], low[*v]); } if (num[u] == low[u]) { int v; do v = stack.top(), stack.pop(), label[v] = size; while (v != u); label[u] = size++; } } inline int count(const AdjacencyList &g) { const int n = g.size(); int index = 0, size = 0, *num = new int[n], *low = new int[n], *label = new int[n]; fill_n(num, n, 0), fill_n(low, n, 0), fill_n(label, n, -1); stack<int> stack; for (int i = 0; i < n; ++i) if (label[i] == -1) visit(g, label, stack, i, num, low, index, size); bool *root = new bool[size]; fill_n(root, size, true); for (int i = 0; i < n; ++i) for(vit j = g[i].begin(); j != g[i].end(); ++j) if(label[i] != label[*j]) root[label[*j]] = false; int c = 0; for (int i = 0; i < size; ++i) if (root[i]) c++; return c; } int main() { int T; cin >> T; for (int _case = 1; _case <= T; ++_case) { int N, M; cin >> N >> M; vector< vector<int> > g(N); for (int i = 0; i < M; ++i) { int a, b; cin >> a >> b; g[--a].push_back(--b); } cout << "Case " << _case << ": " << count(g) << endl; } }
作成者 Toru Mano
最終更新時刻 2023-01-01 (c70d5a1)