UVa Live Archive 6266 - Admiral
問題
ICPC Europe Northwestern Regional 2012 A
V頂点E辺の有向グラフが与えられる。このグラフ上で始点から終点への2本のパスを作る。ただし、2つのパスは始点と終点以外で同じ頂点を共有しないようにする。このとき、パスに使用される辺のコストの総和を最小化せよ。
問題リンク:UVa Live Archive 6266 - Admiral
制約
解法
始点から終点へ流量2を流すときの最小費用流として求めることができます。
2つのパスが頂点を共有しないようにするためには、頂点の容量を1とすればよいです。頂点に容量を設定するには、頂点を入力用と出力用に分け、その2つを設定したい容量でつなぎます。
計算量は です。
Code
最小費用流は蟻本p.203ほぼそのままです。
#include <bits/stdc++.h> using namespace std; typedef pair<int, int> P; constexpr int IINF = INT_MAX; struct edge{ int to, cap, cost, rev; }; int V, E; vector<vector<edge> > G; vector<int> h, dist, prevv, preve; void add_edge(int from, int to, int cap, int cost){ G[from].push_back((edge){to, cap, cost, int(G[to].size())}); G[to].push_back((edge){from, 0, -cost, int(G[from].size())-1}); } // 最小費用流 int min_cost_flow(int s, int t, int f){ int res = 0; fill(h.begin(), h.end(), 0); while(f > 0){ priority_queue<P, vector<P>, greater<P> > que; fill(dist.begin(), dist.end(), IINF); dist[s] = 0; que.push({0,s}); while(!que.empty()){ P p = que.top(); que.pop(); int v = p.second; if(dist[v] < p.first) continue; for(int i=0;i<int(G[v].size());i++){ auto &e = G[v][i]; if(e.cap > 0 && dist[e.to] > dist[v] + e.cost + h[v] - h[e.to]){ dist[e.to] = dist[v] + e.cost + h[v] - h[e.to]; prevv[e.to] = v; preve[e.to] = i; que.push({dist[e.to], e.to}); } } } if(dist[t] == IINF){ return -1; } for(int v=0; v<V*2; v++) h[v] += dist[v]; int d = f; for(int v=t; v!=s; v=prevv[v]){ d = min(d, G[prevv[v]][preve[v]].cap); } f -= d; res += d*h[t]; for(int v=t; v!=s; v=prevv[v]){ auto &e = G[prevv[v]][preve[v]]; e.cap -= d; G[v][e.rev].cap += d; } } return res; } int main() { while(cin >> V >> E){ G.clear(); G.resize(V*2); h.resize(V*2); dist.resize(V*2); prevv.resize(V*2); preve.resize(V*2); // 頂点iの出力点i、入力点i+V for(int i=0;i<E;i++){ int a, b, c; cin >> a >> b >> c; a--; b--; add_edge(a, b+V, 1, c); } // 頂点の容量を1とする for(int i=0;i<V;i++){ add_edge(i+V, i, 1, 0); } cout << min_cost_flow(0, V*2-1, 2) << endl; } return 0; }