P1433 吃乳酪(狀壓dp)
阿新 • • 發佈:2020-08-07
狀壓dp
狀壓\(dp\)可以解決\(n<=21\)的情況。
在狀壓時\(dp[i][j]\),代表在第\(i\)個位置時且走過二進位制狀態\(j\)的最佳答案。
將狀態壓成二進位制的形式去求解。
例:10100110代表經歷了2、3、6、8四種狀態。
時間複雜度\(O(n^2 2^n)\)
題目描述
房間裡放著 n*n塊乳酪。一隻小老鼠要把它們都吃掉,問至少要跑多少距離?老鼠一開始在 (0,0)(0,0) 點處。
輸入格式
第一行有一個整數,表示乳酪的數量 nn。
第 22 到第 (n + 1)(n+1) 行,每行兩個實數,第 (i + 1)(i+1) 行的實數分別表示第 ii 塊乳酪的橫縱座標 \(x_i, y_i\)
輸出格式
輸出一行一個實數,表示要跑的最少距離,保留 2 位小數。
思路
先將每條邊的距離都預處理。
還需要預處理第i個乳酪到第i個乳酪的距離。這裡直接將\((0,0)\)的位置也加了進去,也就是用來初始化了。
for(int i = 1; i <= n; ++i) {
dp[i][1 << (i - 1)] = f[0][i];
}
接下來三層迴圈。
分別列舉二進位制的狀態、當前點所在的位置和能在當前狀態下轉移到當前點的位置。
第二層迴圈需要判斷一下\(i\)在當前二進位制狀態下是否已走過,如果根本沒走過則不需要進行接下來的計算。
第三層迴圈判斷當前點是否已走過,且當前點不和\(i\)
轉移方程:
\[dp[i][k]=min(dp[i][k],dp[j][k-(1<<(i-1))]+f[i][j]) \]
\(k\)表示此事的二進位制狀態(指已經走過哪些點),起點為\(j\),終點為\(i\)。
最大需要的單獨二進位制狀態為\((1<<(n-1))\),但是所有的二進位制狀態和為\((1<<n)-1\)
#include <bits/stdc++.h> #define INF 0x3f3f3f3f #define DOF 0x7f7f7f7f #define endl '\n' #define mem(a, b) memset(a, b, sizeof(a)) #define debug(case, x) cout << case << " : " << x << endl #define open freopen("ii.txt", "r", stdin) #define close freopen("oo.txt", "w", stdout) #define IO \ ios::sync_with_stdio(false); \ cin.tie(0); \ cout.tie(0) #define pb push_back using namespace std; //#define int long long #define lson rt << 1 #define rson rt << 1 | 1 typedef long long ll; typedef pair<int, int> pii; typedef pair<long long, long long> PII; const int maxn = 1e6 + 10; double f[20][20]; double x[20], y[20]; double dp[18][(1 << 15) + 5]; double dis(int i, int j) { return sqrt((x[i] - x[j]) * (x[i] - x[j]) + (y[i] - y[j]) * (y[i] - y[j])); } int main() { double ans = 1e18; for(int i = 1; i <= 15; ++i) { for(int j = 1; j <= ((1 << 15) + 8); ++j) { dp[i][j] = 1e18; } } int n; scanf("%d", &n); x[0] = y[0] = 0; for(int i = 1; i <= n; ++i) { scanf("%lf%lf", &x[i], &y[i]); } for(int i = 0; i < n; ++i) { for(int j = i + 1; j <= n; ++j) { f[i][j] = f[j][i] = dis(i, j); } } for(int i = 1; i <= n; ++i) { dp[i][1 << (i - 1)] = f[0][i]; } for(int k = 1; k < (1 << n); ++k) { for(int i = 1; i <= n; ++i) { if((k & (1 << (i - 1))) == 0) continue; for(int j = 1; j <= n; ++j) { if(i == j) continue; if((k & (1 << (j - 1))) == 0) continue; dp[i][k] = min(dp[i][k], dp[j][k - (1 << (i - 1))] + f[i][j]); } } } for(int i=1;i<=n;++i){ ans=min(ans,dp[i][(1<<n)-1]); } printf("%.2f\n",ans); }