NOSSの雑記

主に競プロでやったことを書きます。

UVa Live Archive 6266 - Admiral

問題

ICPC Europe Northwestern Regional 2012 A

V頂点E辺の有向グラフが与えられる。このグラフ上で始点から終点への2本のパスを作る。ただし、2つのパスは始点と終点以外で同じ頂点を共有しないようにする。このとき、パスに使用される辺のコストの総和を最小化せよ。

問題リンク:UVa Live Archive 6266 - Admiral

制約

  •  3 \le V \le 10^{3}
  •  3 \le E \le 10^{4}

解法

始点から終点へ流量2を流すときの最小費用流として求めることができます。

2つのパスが頂点を共有しないようにするためには、頂点の容量を1とすればよいです。頂点に容量を設定するには、頂点を入力用と出力用に分け、その2つを設定したい容量でつなぎます。

計算量は  O(E \log V) です。

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;
}