1. 程式人生 > 其它 >洛谷 P1336 吃乳酪(狀壓DP)

洛谷 P1336 吃乳酪(狀壓DP)

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;
}