WUPC2nd E - 独立記念日
問題
日本語なので本文参照(http://wupc2nd.contest.atcoder.jp/tasks/wupc_05)
閉路を高々1つ含む、重みつき無向グラフが与えられる。
このグラフの辺をいくつか切断して、グラフがk個以上の連結成分に分かれるようにしたい。
切断する辺の重みの和の最小値はいくつか。
制約条件
グラフの頂点≦100
辺≦100
方針
閉路の辺を切断する場合、しない場合で場合分け。
閉路の辺を切断する場合、切断する辺をひとつ決めるとグラフは森になる。
なので、森はひとつ辺を切断するたびに連結成分の個数は1つ増えるので、
貪欲にコストの安い辺から切断すればいい。
閉路の辺を切断しない場合、閉路以外の辺を1本切断すれば、連結成分の個数は1増えるので
コストの安い辺から切断すればいい。
ちなみに、閉路が任意の個数含まれている問題はNP困難であるらしい。
証明を考えてみたけど、うまく示せなかった。。。
ソースコード
int p[100]; int root(int x){ if(x == p[x]) return x; return p[x] = root(p[x]); } int n, m, k; vector<pair<int, pi> > es; vector<vector<pi> > e; //to, id int id[100][100]; bool v[100]; int st[110], sz; bool isloop[10000]; vi loop; void rec(int c, int p){ v[c] = 1; st[sz++] = c; rep(i, e[c].size()) if(p != e[c][i].first){ if(v[e[c][i].first] && loop.empty()){ for(int j = sz - 1; j >= 0; j--){ loop.pb(st[j]); if(st[j] == e[c][i].first) break; } } else if(!v[e[c][i].first]){ rec(e[c][i].first, c); } } sz--; } int main(){ cin >> n >> m >> k; e.resize(n); memset(id, -1, sizeof(id)); int cnt = n; rep(i, n) p[i] = i; rep(i, m){ int a, b, c; cin >> a >> b >> c; a--; b--; es.pb(mp(c, mp(a, b))); e[a].pb(mp(b, i)); e[b].pb(mp(a, i)); id[a][b] = id[b][a] = i; a = root(a); b = root(b); if(a != b){ cnt--; p[a] = b; } } k = max(0, k - cnt); rep(i, n) if(!v[i]){ sz = 0; rec(i, i); } /* dbg(loop.size()); rep(i, loop.size()) dbg(loop[i]); */ vi edge; rep(i, loop.size()){ edge.pb(id[loop[i]][loop[(i + 1) % loop.size()]]); isloop[edge.back()] = 1; assert(edge.back() >= 0); } ll ans = 1ll << 60; rep(i, edge.size()){ ll tmp = es[edge[i]].first; vi ee; rep(j, m) if(edge[i] != j) ee.pb(es[j].first); sort(all(ee)); rep(j, k) tmp += ee[j]; ans = min(ans, tmp); } ll tmp = 0; vi ee; rep(i, m) if(!isloop[i]) ee.pb(es[i].first); sort(all(ee)); if(ee.size() < k) tmp = 1ll << 60; else rep(i, k) tmp += ee[i]; ans = min(ans, tmp); cout << ans << endl; return 0; }