1. 程式人生 > >洛谷 P1433 DP 狀態壓縮

洛谷 P1433 DP 狀態壓縮

main 二進制表示 out str cstring 等於 ios 二進制 老鼠

題目描述

房間裏放著n塊奶酪。一只小老鼠要把它們都吃掉,問至少要跑多少距離?老鼠一開始在(0,0)點處。

輸入輸出格式

輸入格式:

第一行一個數n (n<=15)

接下來每行2個實數,表示第i塊奶酪的坐標。

兩點之間的距離公式=sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2))

輸出格式:

一個數,表示要跑的最少距離,保留2位小數。

輸入輸出樣例

輸入樣例#1:
4
1 1
1 -1
-1 1
-1 -1
輸出樣例#1:
7.41

這題開始用搜索做的,稍微剪下枝就能水過去,不過時間用的比較多,後來用 DP 做了一遍。

用二進制表示一個集合,比如 1011 表示一個包含了 0,1,3 結點的集合。

dp[i][j] 表示,在 i 集合中,以 j 結點作為起始點走完集合中所有點的最短路徑。

dp[i][j] = min(dp[k][x] + len[j][x])

k 是去掉 j 結點的集合,x 是 k 中的任意一點, len[j][x] 表示 j 到 x 的距離。

代碼:

#include <iostream>
#include <cstring>
#include <cmath>
using namespace std;

const int MAX = 17;
const int INF = 0x3fffffff;

double dp[1<<MAX][MAX];        //
i 集合,以 j 為起始點,最短的路徑 double r[MAX], c[MAX], len[MAX][MAX]; int n; int main(){ // freopen("input.txt", "r", stdin); scanf("%d", &n); r[0] = c[0] = 0; for(int i=1; i<=n; i++){ scanf("%lf%lf", &r[i], &c[i]); } //求出兩點之間的長度 for(int i=0; i<=n; i++){
for(int j=0; j<=n; j++){ double x1 = r[i], y1 = c[i]; double x2 = r[j], y2 = c[j]; len[i][j] = sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2)); } } double ans = 999999999; //DP //初始化 for(int i=0; i<(1<<MAX); i++) for(int j=0; j<MAX; j++) dp[i][j] = 999999999; for(int i=0; i<=n; i++) //只有一個結點時,最短路徑為 0 dp[(1<<i)][i] = 0; for(int i=1; i<=((1<<(n+1))-1); i++){ //枚舉集合 i for(int j=0; j<=n; j++){ int t = (1<<j); if((t & i) > 0){ //如果 i 結合有第 j 個結點 int k = i - t; //k 集合等於 i 集合去掉 j 結點 // if(i == 3){ // cout << k << endl; // } for(int x=0; x<=n; x++){ t = (1<<x); if((t & k) > 0){ //如果 k 集合裏有第 x 個結點 //dp[k][x] + len[x][j] : 從 j 點出發到 x 點的距離再加上從 x 出發走完 k 集合的最短距離 dp[i][j] = min(dp[i][j], dp[k][x] + len[j][x]); } } } } } ans = dp[((1<<(n+1))-1)][0]; printf("%.2lf", ans); return 0; }

洛谷 P1433 DP 狀態壓縮