洛谷P1433吃奶酪
吃奶酪
题目描述
房间里放着 \(n\) 块奶酪。一只小老鼠要把它们都吃掉,问至少要跑多少距离?老鼠一开始在 \((0,0)\) 点处。
输入格式
第一行有一个整数,表示奶酪的数量 \(n\)。
第 \(2\) 到第 \((n + 1)\) 行,每行两个实数,第 \((i + 1)\) 行的实数分别表示第 \(i\) 块奶酪的横纵坐标 \(x_i, y_i\)。
输出格式
输出一行一个实数,表示要跑的最少距离,保留 \(2\) 位小数。
样例 #1
样例输入 #1
4
1 1
1 -1
-1 1
-1 -1
样例输出 #1
7.41
提示
数据规模与约定
对于全部的测试点,保证 \(1\leq n\leq 15\),\(|x_i|, |y_i| \leq 200\),小数点后最多有 \(3\) 位数字。
提示
对于两个点 \((x_1,y_1)\),\((x_2, y_2)\),两点之间的距离公式为 \(\sqrt{(x_1-x_2)^2+(y_1-y_2)^2}\)。
\(2022.7.13\):新增加一组 \(\text{Hack}\) 数据。
这是一道比较明显的状压dp
状态表示 f[i][j]:在第j个点,当前状态为i的所有路径,属性:最小值
状态计算:可以从前往后推,也可以从后往前考虑,从前往后考虑的话,枚举j这个点可能由哪些点转移过来f[i |(1<<(k - 1))][k] = min(f[i |(1<<(k - 1))][k], f[i][j] + a[j][k]);
#include <iostream>
#include <algorithm>
#include <cstring>
#include <math.h>
using namespace std;
const int N = 20, M = 1 << N;
double f[M][N], a[N][N];
struct point
{
double x, y;
}p[N];
int main()
{
// freopen("1.in","r",stdin);
int n;cin >> n;
for(int i = 0; i < (1 << n); ++ i)
for(int j = 0; j <= n; ++j)
f[i][j] = 2e9;
for(int i = 1; i <= n; ++i)
{
//x,y均为实数
double x, y; cin >> x >> y;
p[i] = {x, y};
}
//预处理出来每个点到每个点的距离
for(int i = 0; i <= n; ++i)
for(int j = 0; j <= n; ++ j)
a[i][j] = sqrt((p[i].x - p[j].x) * (p[i].x - p[j].x) + (p[i].y - p[j].y) * (p[i].y - p[j].y));
//因为第0个转移时不符合状态转移方程,这时我们单独跑一遍0到每个点的状态
for(int i=1;i<=n;++i) f[1<<(i-1)][i]=a[0][I];
//此时我们只用考虑正常的转移
for(int i=0;i<(1<<n);++i)
{
for(int j=1;j<=n;++j)
//只有当第j位为1时状态i才表示已经在第j个点了
if(i&(1<<(j-1)))
{
for(int k=1;k<=n;++k)
//只有当状态i的第k位为0也就是说还没有到达过,我们才用k去转移
if(!(i&(1<<(k-1)))) f[i|(1<<(k-1))][k]=min(f[i|(1<<(k-1))][k],f[i][j]+a[j][k]);
}
}
double res = 2e9;
//找到符合状态的所有值中的最小值
for(int i = 1; i <= n; ++ i) res = min(res, f[(1<<n)-1][i]);
printf("%.2lf",res);
return 0;
}