洛谷 P1336 吃乳酪(狀壓DP)
阿新 • • 發佈:2021-09-05
P1336 吃乳酪
題意
房間中放有n塊乳酪。一隻小老鼠(位於[0, 0])要把它們都吃掉,問最少要跑多少距離。
輸入
第一行一個整數,表示乳酪數量 \(n\) 。
第 \(2\) 行到第 \((n \ + \ 1)\) 行,每行兩個實數 \(x \ , \ y\) ,第 \((i \ + \ 1)\) 行表示第 \(i\) 塊乳酪的位置 \([x, \ y]\)。
輸出
4
1 1
1 -1
-1 1
-1 -1
7.41
題目分析
狀態壓縮, \(f(i, \ s)\) 表示從i點開始,走完s集合中所有點的最短路徑。最後再加上從初始點到 \(i\) 點的距離即可。
Code
#include <bits/stdc++.h> using namespace std; const int N = 16; int n; double x[N], y[N], dis[N][N]; double f[N][1 << N]; // f(i, s) 從i出發到達s集合所有點的最小距離 void get_dis () { for (int i = 0; i <= n; i ++ ) for (int j = 0; j <= n; j ++ ) dis[i][j] = sqrt((x[i] - x[j]) * (x[i] - x[j]) + (y[i] - y[j]) * (y[i] - y[j])); } void solve () { cin >> n; for(int i = 1; i <= n; i ++ ) cin >> x[i] >> y[i]; get_dis(); for (int i = 0; i <= n; i ++ ) for (int j = 0; j < (1 << n); j ++ ) f[i][j] = 1e18; // s 集合的所有狀態 for (int s = 0; s < (1 << n); s ++ ) // 從i開始到達集合其他點 for (int i = 1; i <= n; i ++ ) { if ((s & (1 << i-1)) == 0) continue; // s中沒有i if (s == (1 << i-1)) { f[i][s] = 0; continue; } // *** s 中只有一個i點 for (int j = 1; j <= n; j ++ ) { if ((s && (1 << j-1)) == 0) continue; // s 中沒有j點 // 轉移狀態,先從i到達j,然後從j到達其他點 f[i][s] = min(f[i][s], f[j][s - (1 << i-1)] + dis[i][j]); } } double ans = -1; for (int i = 1; i <= n; i ++ ) { double t = f[i][(1 << n) - 1] + dis[i][0]; // 從初始點到i點,再到達其他所有點 if (ans == -1 || ans > t) ans = t; } cout.precision(2); cout << fixed << ans << endl; } signed main () { solve(); return 0; }