房间里放着 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 , y i x_i, y_i xi,yi。
输出一行一个实数,表示要跑的最少距离,保留 2 2 2 位小数。
4
1 1
1 -1
-1 1
-1 -1
7.41
对于全部的测试点,保证 1 ≤ n ≤ 15 1\leq n\leq 15 1≤n≤15, ∣ x i ∣ , ∣ y i ∣ ≤ 200 |x_i|, |y_i| \leq 200 ∣xi∣,∣yi∣≤200,小数点后最多有 3 3 3 位数字。
对于两个点 ( 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。
2022.7.13 2022.7.13 2022.7.13:新增加一组 Hack \text{Hack} Hack 数据。
#include
using namespace std;
int n;
double x, y;
double ans = 10000000;
vector<pair<double, double>> v;
double vis[20];
//状态记录 d[12][42501] 表示走到编号为12的点,所经过的点的编号为42501二进制序列为1的数
double f[20][33000];
//两个点的距离
double calDis(pair<double, double> p1, pair<double, double> p2) {
return sqrt((p1.first - p2.first) * (p1.first - p2.first) +
(p1.second - p2.second) * (p1.second - p2.second));
}
//lastid:上一个坐标的编号,last:上一个点的坐标,status为当前走过的点编号组成的二进制序列的十进制值
void dfs(int u, int lastid, pair<double, double> last, int status, double dis) {
//剪枝
if (dis >= ans)
return;
if (u == n) {
ans = min(ans, dis);
return;
}
for (int i = 0; i < n; i++) {
double xx = v[i].first, yy = v[i].second;
if (!vis[i]) {
//更新状态,因为加上了i这个点,status + 2^i(1左移i位就是2的i的值)
int newStatus = status + (1 << i);
//状态压缩剪枝
if (f[i][newStatus] != 0 && f[i][newStatus] <= f[lastid][status] + calDis(last, {xx, yy}))
continue;
f[i][newStatus] = f[lastid][status] + calDis(last, {xx, yy});
vis[i] = 1;
dfs(u + 1, i, {xx, yy}, newStatus, dis + calDis(last, {xx, yy}));
vis[i] = 0;
}
}
}
int main() {
cin >> n;
for (int i = 0; i < n; i++) {
cin >> x >> y;
v.push_back({x, y});
}
dfs(0, 0, {0, 0}, 0, 0);
printf("%.2lf", ans);
return 0;
}