SRM 492 div1 med: TimeTravellingTour
解法
区間 dp っぽく解くことが出来ます。
まず前処理で, 各頂点間の最短距離を覚えておきましょう。この時, 頂点 0 と cities のどこかの頂点の距離が INF (要するに辿りつけない) だったらその頂点を調べるのは不可能なので -1 を返します。
他の場合は, dp[now][from][to] = (今頂点 now にいて, cities の from から to の頂点を調べたい時の, 最小コスト) とします。
すると, 最小コストは,
now -> v と普通に移動して, v で一定の区間 [from, m) を調べた後, now に(TimeTravel で)戻ってきて, その後の区間 [m, to) を調べる
という (v, m) の組み合わせの最小値で求めることが出来ます。過去に戻る起点として, now を使うイメージですね。
状態数が O(n^3) で, 各状態を求めるのに必要な計算が O(n^2) なので O(n^5) ですが AC することが出来ました。
オーダーを改善するために, dfs を, v を決める側と m を決める側に分けることも出来ます。これは O(n^4) です。
まとめ
- 見た目からは区間 dp を全く想像出来ない…と思ってたけど, よく考えると順列っぽかったら区間 dp を考えるというのはよくある話だった。反省。
O(n^5)
const ll INF = 1ll<<55; const int MAXN = 55; ll d[MAXN][MAXN]; ll dp[MAXN][MAXN][MAXN]; int n, m; vector<int> city; // [from, to) ll dfs(int now, int from, int to) { ll& ret = dp[now][from][to]; if (ret >= 0) return ret; if (from >= to) return ret = 0; if (now == city[from]) return ret = dfs(now, from+1, to); ret = INF; for (int i = 0; i < n; i++) { for (int j = from; j < to; j++) { ret = min(ret, d[now][i] + dfs(i, from, j+1) + dfs(now, j+1, to)); } } return ret; } class TimeTravellingTour { public: long long determineCost(int N, vector <int> cities, vector <string> roads) { n = N; m = cities.size(); city = cities; string ss; for (string s : roads) ss += s; for (char& c : ss) if (c == ',') c = ' '; istringstream is(ss); for (int i = 0; i < n; i++) for (int j = 0; j < n; j++) d[i][j] = INF; { int x, y, w; while (is >> x >> y >> w) d[x][y] = d[y][x] = w; } for (int i = 0; i < n; i++) d[i][i] = 0; for (int k = 0; k < n; k++) for (int i = 0; i < n; i++) for (int j = 0; j < n; j++) { d[i][j] = min(d[i][j], d[i][k] + d[k][j]); } for (int el : cities) { if (d[0][el] >= INF) return -1; } memset(dp, -1, sizeof(dp)); return dfs(0, 0, m); } };
O(n^4)
const ll INF = 1ll<<55; const int MAXN = 55; ll d[MAXN][MAXN]; ll dp[MAXN][MAXN][MAXN]; ll dp2[MAXN][MAXN][MAXN]; int n, m; vector<int> city; ll dfs(int, int, int); ll dfs2(int now, int from, int to) { ll& ret = dp2[now][from][to]; if (ret >= 0) return ret; ret = INF; for (int i = from; i < to; i++) { ret = min(ret, dfs(now, from, i+1) + dfs(now, i+1, to)); } return ret; } // [from, to) ll dfs(int now, int from, int to) { ll& ret = dp[now][from][to]; if (ret >= 0) return ret; if (from >= to) return ret = 0; if (now == city[from]) return ret = dfs(now, from+1, to); ret = INF; for (int i = 0; i < n; i++) { ret = min(ret, d[now][i] + dfs2(i, from, to)); } return ret; } class TimeTravellingTour { public: long long determineCost(int N, vector <int> cities, vector <string> roads) { n = N; m = cities.size(); city = cities; string ss; for (string s : roads) ss += s; for (char& c : ss) if (c == ',') c = ' '; istringstream is(ss); for (int i = 0; i < n; i++) for (int j = 0; j < n; j++) d[i][j] = INF; { int x, y, w; while (is >> x >> y >> w) d[x][y] = d[y][x] = w; } for (int i = 0; i < n; i++) d[i][i] = 0; for (int k = 0; k < n; k++) for (int i = 0; i < n; i++) for (int j = 0; j < n; j++) { d[i][j] = min(d[i][j], d[i][k] + d[k][j]); } for (int el : cities) { if (d[0][el] >= INF) return -1; } memset(dp, -1, sizeof(dp)); memset(dp2, -1, sizeof(dp2)); return dfs(0, 0, m); } };