まず強連結成分分解する。
その後、連結成分を圧縮した森でルートとなっている頂点を探す。
その頂点に含まれる本来の頂点のどれかをそれ単体で作成する。
強連結成分分解のコードを描いたのがだいぶ昔なので、描き直したい衝動に駈られている。
#include <algorithm> #include <iostream> #include <map> #include <numeric> #include <queue> #include <set> #include <stack> #include <vector> #include <sstream> #include <cmath> #include <cstdio> #include <cstdlib> #include <cstring> #include <cassert> #define REP(i, n) for(int i=0; i<(int)n; ++i) #define FOR(i, c) for(__typeof((c).begin())i=(c).begin();i!=(c).end();++i) #define ALL(c) (c).begin(),(c).end() #define each(i, c) FOR(i, c) #define VAR(a) cout << #a << " : " << a << endl; typedef long long int lli; using namespace std; const int inf = 1 << 29; struct Edge{ int src, dst, cost; Edge(int s, int d) : src(s), dst(d), cost(inf) {} Edge(int s, int d, int c) : src(s), dst(d), cost(c) {} }; typedef vector<Edge> Edges; typedef vector<Edges> Graph; void visit(const Graph &G, int n, int order[], int &time, bool visited[]) { visited[n] = true; FOR(e, G[n]){ if(!visited[e->dst]) visit(G, e->dst, order, time, visited); } order[--time] = n; return ; } vector< vector<int> > detectSCC(const Graph &G) { const int size = G.size(); bool visited[size]; int order[size], scc[size], time, prev = size - 1; vector< vector<int> > S; Graph H(size); fill(order, order + size, -1); fill(scc, scc + size, -1); REP(i, size){ FOR(e, G[i]) H[e->dst].push_back(Edge(e->dst, e->src)); } time = size; fill(visited, visited + size, false); REP(i, size){ if(!visited[i]) visit(G, i, order, time, visited); } time = size; fill(visited, visited + size, false); REP(i, size){ if(!visited[order[i]]){ visit(H, order[i], scc, time, visited); int j = prev; vector<int> T; for(j=prev; 0 <= j && scc[j] != -1; --j) T.push_back(scc[j]); prev = j; S.push_back(T); } } return S; } int main(int argc, char *argv[]) { int node; while (cin >> node && node) { map<string, int> name; Graph g(node); int cost[node]; for (int i = 0, cnt = 0; i < (int)node; ++i) { string s, t; int a, b; cin >> s >> a >> t >> b; if (name.count(s)) ; else name[s] = cnt++; if (name.count(t)) ; else name[t] = cnt++; int dst = name[s]; int src = name[t]; g[src].push_back(Edge(src, dst, b)); cost[dst] = a; } vector< vector<int> > scc = detectSCC(g); map<int, int> rename; for (int i = 0; i < (int)scc.size(); ++i) { for (int j = 0; j < (int)scc[i].size(); ++j) { rename[scc[i][j]] = i; } } int sum = 0; int mn[scc.size()]; fill(mn, mn + scc.size(), inf); bool root[scc.size()]; fill(root, root + scc.size(), true); for (int i = 0; i < (int)node; ++i) { each (e, g[i]) { int src = rename[e->src]; int dst = rename[e->dst]; if (src == dst) { mn[dst] = min(mn[dst], cost[e->dst] - e->cost); } else { root[dst] = false; } sum += e->cost; } } for (int i = 0; i < (int)scc.size(); ++i) { if (root[i]) sum += mn[i]; } cout << sum << endl; } return 0; }