ふーらくたるの雑の記

雑な事を記していくなど

AOJ2732 - Modern Announce Network

解法

入力より生徒を頂点、コネクションを重み1の辺とするグラフを構築することができます。このようにして得られたグラフから、さらに各学年の生徒を一つの頂点にまとめたグラフ G = (V, E)を考えます。あらかじめネットワークを構築しておけば、スタートとなる頂点は任意に選べることから、解くべき問題は今まとめた3個の頂点をターミナル集合 Tとする最小シュタイナー木問題となります。最小シュタイナー木問題はNP困難な問題であることが知られていますが、今回はターミナルが3つであることから多項式時間解法が存在します。

証明は省略しますがターミナルが3つの場合、最小シュタイナー木はパスグラフまたはY字型のグラフにしかなり得ません。よって各ターミナル t \in Tについて、 tを始点、( tから vまでの距離、経路中の最小の頂点番号)を値とする最短経路を求めます。そして各頂点 v \in Vに対して、各ターミナルからの距離の和と、含みうる頂点番号の最小値を計算します。この中で最適なものが解となります。

ソースコード

#include <algorithm>
#include <cstring>
#include <iostream>
#include <map>
#include <queue>
#include <utility>
#include <vector>

using namespace std;

using P = pair<int, int>; // (距離,パス上の最小頂点id)
using PPI = pair<P, int>;

const int inf = 1 << 28;

int N, A, B, C;

P cost[3][20000];
vector<int> G[20000];
int old_id[20000]; // 変換後のグラフG'の頂点v'に対応する元の頂点v

void dijkstra(int s) {
    for (int v = 0; v < N; v++) {
        cost[s][v] = {inf, inf};
    }

    cost[s][s] = {0, old_id[s]};

    priority_queue<PPI, vector<PPI>, greater<PPI>> Q;
    Q.push({{0, old_id[s]}, s});
    while (!Q.empty()) {
        auto q = Q.top();
        Q.pop();
        P val = q.first;
        int v = q.second;

        if (cost[s][v] < val) {
            continue;
        }

        for (int to : G[v]) {
            P nval = make_pair(val.first + 1, min(val.second, old_id[to]));
            if (cost[s][to] > nval) {
                cost[s][to] = nval;
                Q.push({nval, to});
            }
        }
    }
}

int main() {
    static int num[3];
    cin >> N;
    for (int i = 0; i < 3; i++) {
        cin >> num[i];
    }
    static int min_id[3] = {inf, inf, inf};

    static int group[20000];
    memset(group, -1, sizeof(group));
    for (int i = 0; i < 3; i++) {
        for (int j = 0; j < num[i]; j++) {
            int x;
            cin >> x;
            x--;
            group[x] = i;
            min_id[i] = min(min_id[i], x);
        }
    }
    map<int, int> mp;
    int id = 3;

    for (int i = 0; i < 3; i++) {
        old_id[i] = min_id[i];
    }

    for (int v = 0; v < N; v++) {
        if (0 <= group[v] and group[v] < 3)
            mp[v] = group[v];
        else {
            mp[v] = id++;
            old_id[mp[v]] = v;
        }
    }

    int M;
    cin >> M;
    for (int i = 0; i < M; i++) {
        int x, y;
        cin >> x >> y;
        x--;
        y--;

        G[mp[x]].emplace_back(mp[y]);
        G[mp[y]].emplace_back(mp[x]);
    }

    // Dijkstra
    for (int s = 0; s < 3; s++) {
        dijkstra(s);
    }

    P ans = {inf, inf};
    for (int v = 0; v < id; v++) {
        ans = min(ans, {cost[0][v].first + cost[1][v].first + cost[2][v].first,
                        min({cost[0][v].second, cost[1][v].second,
                             cost[2][v].second})});
    }
    cout << ans.first << " " << ans.second + 1 << endl;

    return 0;
}

感想

某企画の4問目です。femtoさんとペアプロしながら通しました。三点を結ぶ最小ネットワークの構築する問題としてはARC019C - 最後の森があります。