题目
问题描述
房间里放着 n n n 块奶酪。一只小老鼠要把它们都吃掉,问至少要跑多少距离?老鼠一开始在 ( 0 , 0 ) (0,0) (0,0) 点处。
输入格式
- 第一行有一个整数,表示奶酪的数量 n n n。
- 第 2 2 2 到第 ( n + 1 ) (n + 1) (n+1) 行,每行两个实数,第 ( i + 1 ) (i + 1) (i+1) 行的实数分别表示第 i i i 块奶酪的横纵坐标 x i x_i xi, y i y_i yi。
输出格式
- 输出一行一个实数,表示要跑的最少距离,保留 2 2 2 位小数。
数据范围
1 ≤ n ≤ 15 , ∣ x i ∣ , ∣ y i ∣ ≤ 200 1≤n≤15,|x_i|, |y_i| \leq 200 1≤n≤15,∣xi∣,∣yi∣≤200
输入/输出例子
输入
4
1 1
1 -1
-1 1
-1 -1
输出
7.41
说明/提示
对于两个点 ( x 1 , y 1 ) (x_1,y_1) (x1,y1), ( x 2 , y 2 ) (x_2, y_2) (x2,y2),
两点之间的距离公式为 ( x 1 − x 2 ) 2 + ( y 1 − y 2 ) 2 \sqrt{(x_1-x_2)^2+(y_1-y_2)^2} (x1−x2)2+(y1−y2)2
解题思路
分析
考虑状压DP,定义状态
f
i
,
j
f_{i,j}
fi,j:
当前在
i
i
i 点,走过的点的状态为
j
j
j 时的距离。
我们先枚举状态
i
i
i,再枚举当前节点
j
j
j,然后枚举由哪个节点
k
k
k 转移过来的。
需要注意的是:
- 由于题目没有规定第一个经过的节点,所以每个节点都可以作为起点。
- 算完距离之后,不要忘记加上从 0 0 0 节点到 i i i 节点的距离。
- 不要用 memset 给 d o u b l e double double 类型赋初值。
- 注意开的空间大小。
处理完这些之后,就是裸的状压DP了,直接套模板即可。
Code
#include<bits/stdc++.h>
#define int long long
#define IOS ios::sync_with_stdio(false),cin.tie(NULL),cout.tie(NULL);
using namespace std;
const int N=1<<16;
typedef double lf;
int n;
lf x[N],y[N],f[16][N];
lf ga(int i,int j)
{
lf sum=sqrt((x[i]-x[j])*(x[i]-x[j])+(y[i]-y[j])*(y[i]-y[j]));
return sum;
}
signed main()
{
IOS;
cin>>n;
for(int i=1;i<=n;i++)
cin>>x[i]>>y[i];
for(int i=0;i<=15;i++)
fill(f[i]+1,f[i]+N,10000);
for(int i=1;i<(1<<n);i++)
{
for(int j=1;j<=n;j++)
{
if(i&(1<<(j-1)))
{
if(i==(1<<(j-1)))
f[j][i]=0;
for(int k=1;k<=n;k++)
if(k!=j&&((1<<(k-1))&i))
f[j][i]=min(f[j][i],f[k][i-(1<<(j-1))]+ga(j,k));
}
}
}
lf ans=1e9;
for(int i=1;i<=n;i++)
ans=min(ans,f[i][(1<<n)-1]+ga(0,i));
cout<<fixed<<setprecision(2)<<ans;
return 0;
}