洛谷 P1336 吃奶酪
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;
}