狀態壓縮DP將狀態作爲數組的一維,進行動態規劃。
房間裏放着 塊奶酪。一隻小老鼠要把它們都喫掉,問至少要跑多少距離?老鼠一開始在 點處。
輸出要跑的最少距離。
其中。
因爲要把所有的奶酪喫完,所以我們要考慮喫的順序,不同的順序對結果有很大的影響。從暴力的角度看,共有個順序,當時,,顯然會超時。
優化,考慮如下情況:
假設現在已經吃了塊奶酪,要去喫剩下的塊。已經喫的塊中只有最後一塊,對剩下的塊的按什麼順序喫有影響。如前三塊編號爲,,, 那麼(先喫第塊,再喫第塊,最後喫第塊),對剩下塊按什麼順序喫是沒有影響的。也就是說,我們對於已經喫過的奶酪,只關心吃了哪幾塊,和最後一塊吃了哪一塊。由此,可以減少狀態數。
剛剛介紹的也是狀態壓縮的基本思想。現在開始介紹狀態壓縮,設表示位長度的二進制,第位(從右往左數,從開始)爲時表示吃了第塊奶酪,爲時表示還沒喫第塊奶酪。
如,在二進制表示下爲,表示已經吃了第塊,第塊,第塊奶酪,沒有喫第塊,第塊,第塊,第塊奶酪;
如,在二進制表示下爲,表示已經吃了第塊,第塊,第塊奶酪,沒有喫第塊,第塊,第塊,第塊奶酪。
那麼DP的狀態表示在狀態,最後一塊喫的是下的最小距離和。
輸出吃了所有奶酪,最後一個喫的是下取一個最小值:
其中二進制下的表示個,即吃了所有奶酪。
轉移式:
其中表示下標爲和的距離;要包含和兩個狀態,即 and .
初始化:
;
其餘點爲無窮大。
代碼:
/* ***********************************************
Author : VFVrPQ
Created Time : 三 3/ 4 14:21:47 2020
File Name : luogu1433喫奶酪.cpp
Problem :
Description :
Solution :
Tag :
************************************************ */
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <string>
#include <cmath>
#include <cstdlib>
#include <vector>
#include <queue>
#include <stack>
#include <set>
#include <map>
#include <iomanip>
using namespace std;
#define DEBUG(x) cout<<x<<endl;
const int N = 17;
const int M = 1e9+7;
const int INF = 1e9+7;
const double eps = 1e-8;
int n;
double x[N], y[N];
double dp[1<<N][N];
double dis(double x1, double y1, double x2, double y2){
return sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2));
}
int main()
{
scanf("%d",&n);
for (int i=0;i<n;i++){
scanf("%lf%lf",&x[i],&y[i]);
}
//初始化
int maxmask = 1<<n;
for (int mask=0;mask<maxmask;mask++){
for (int i=0;i<n;i++) dp[mask][i] = INF; // 初始化爲極小值
}
for (int i=0;i<n;i++) dp[1<<i][i] = dis(0.0,0.0,x[i],y[i]);//走一步的
for (int mask=0;mask<maxmask;mask++){
for (int i=0;i<n;i++)if (mask&(1<<i)){
for (int j=0;j<n;j++)if (mask&(1<<j)){
if (fabs(dp[mask-(1<<i)][j]-INF)<eps) continue; //
dp[mask][i] = min(dp[mask][i], dp[mask-(1<<i)][j] + dis(x[i], y[i], x[j], y[j]));
}
}
}
double ans = INF;
for (int i=0;i<n;i++) ans = min(ans, dp[maxmask-1][i]);
printf("%.2lf\n", ans);
return 0;
}