https://atcoder.jp/contests/past202005-open/tasks/past202005_m
前提知識
解説
https://atcoder.jp/contests/past202005-open/submissions/14069459
制約でK≦16というのがあるので、戦略的にここから取り組む。
bitDPだろうと仮定が立ち、問題から
dp[msk][lst] := 行商人が既にmskの町を訪れていて最後にlstの町にいるときの通行料の合計
を使って計算するのだろうと思う。
この仮定が正しいとすると、嬉しいことがある。
それは、街sと街t1,t2,...,tK以外の街はdpする上では無視して構わないことになる。
この街の間での最小通行料が分かっていれば、DPは解くことができる。
最小通行料、最短距離と言えばダイクストラかワーシャルフロイドであるが、
ワーシャルフロイドは全頂点間を網羅できるが、O(N3)で間に合わない。
だが、今回は街sと街t1,t2,...,tKからの最短距離が分かればいいので、
それぞれでダイクストラを行うことで、DPで必要な間での最短距離を求めよう。
前提アルゴリズムが分かっていればそこまで難しくはないが、
ダイクストラを複数回行う事になれていないと、思いついてもすぐに取り組めないかもしれない。
int N, M; vector<pair<int, int>> E[101010]; int s, K, t[16]; ll dp[1 << 16][16]; //--------------------------------------------------------------------------------------------------- template<typename T> using min_priority_queue = priority_queue<T, vector<T>, greater<T>>; int vis[101010]; void dijk(int s, vector<ll>& D) { rep(i, 0, N) D[i] = infl; rep(i, 0, N) vis[i] = 0; min_priority_queue<pair<ll, int>> que; D[s] = 0; que.push({ 0, s }); while (!que.empty()) { auto q = que.top(); que.pop(); ll cst = q.first; int cu = q.second; if (vis[cu]) continue; vis[cu] = 1; fore(p, E[cu]) { ll cst2 = cst + p.second; int to = p.first; if (chmin(D[to], cst2)) que.push({ D[to], to }); } } } //--------------------------------------------------------------------------------------------------- void _main() { cin >> N >> M; rep(i, 0, M) { int a, b; cin >> a >> b; a--; b--; E[a].push_back({ b, 1 }); E[b].push_back({ a, 1 }); } cin >> s >> K; s--; rep(i, 0, K) cin >> t[i], t[i]--; vector<ll> fromS(N); dijk(s, fromS); vector<vector<ll>> fromTs(K, vector<ll>(N)); rep(k, 0, K) dijk(t[k], fromTs[k]); rep(msk, 0, 1 << K) rep(lst, 0, K) dp[msk][lst] = infl; rep(k, 0, K) dp[1 << k][k] = fromS[t[k]]; rep(msk, 0, 1 << K) rep(lst, 0, K) if (dp[msk][lst] < infl) { rep(nxt, 0, K) if (!(msk & (1 << nxt))) { chmin(dp[msk + (1 << nxt)][nxt], dp[msk][lst] + fromTs[lst][t[nxt]]); } } ll ans = infl; rep(lst, 0, K) chmin(ans, dp[(1 << K) - 1][lst]); cout << ans << endl; }