《P1433 吃奶酪》
题目描述
房间里放着 n 块奶酪。一只小老鼠要把它们都吃掉,问至少要跑多少距离?老鼠一开始在 (0,0) 点处。
输入格式
第一行有一个整数,表示奶酪的数量 n。
第 2 到第 (n+1) 行,每行两个实数,第 (i+1) 行的实数分别表示第 i 块奶酪的横纵坐标 xi,yi。
输出格式
输出一行一个实数,表示要跑的最少距离,保留 2 位小数。
输入输出样例
输入 #1复制
4 1 1 1 -1 -1 1 -1 -1
输出 #1复制
7.41
说明/提示
数据规模与约定
对于全部的测试点,保证 1≤n≤15,∣xi∣,∣yi∣≤200,小数点后最多有 3 位数字。
提示
对于两个点 (x1,y1),(x2,y2),两点之间的距离公式为 (x1−x2)2+(y1−y2)2。
2022.7.13:新增加一组 Hack 数据。
代码实现:
#include <iostream>
#include <cmath>
#include <cstring>
using namespace std;
const int N = 15;
const double INF = 1e18;
double x[N], y[N];
double dist[N][N]; // dist[i][j] 表示奶酪i到奶酪j的距离(i,j从0开始,0为原点?不,原点单独处理)
// 原点是起点,奶酪从0到n-1编号,共n个奶酪,状态压缩n位对应0~n-1
double dp[1 << N][N]; // dp[state][u]:已访问state中的奶酪,最后位于奶酪u时的最短距离
int main() {
int n;
cin >> n;
for (int i = 0; i < n; i++) {
cin >> x[i] >> y[i];
}
// 计算原点到各奶酪的距离,以及奶酪间的距离
double origin_dist[N]; // 原点到奶酪i的距离
for (int i = 0; i < n; i++) {
origin_dist[i] = sqrt(x[i] * x[i] + y[i] * y[i]);
for (int j = 0; j < n; j++) {
double dx = x[i] - x[j];
double dy = y[i] - y[j];
dist[i][j] = sqrt(dx * dx + dy * dy);
}
}
// 初始化DP数组:初始时未访问任何奶酪,从原点出发到奶酪u
memset(dp, 0x3f, sizeof(dp));
for (int u = 0; u < n; u++) {
dp[1 << u][u] = origin_dist[u]; // 访问奶酪u,距离为原点到u的距离
}
// 遍历所有状态
for (int state = 1; state < (1 << n); state++) {
for (int u = 0; u < n; u++) {
if (!(state & (1 << u))) continue; // u未被访问,跳过
for (int v = 0; v < n; v++) {
if (state & (1 << v)) continue; // v已被访问,跳过
int new_state = state | (1 << v);
// 从奶酪u到奶酪v,更新new_state下v的距离
if (dp[new_state][v] > dp[state][u] + dist[u][v]) {
dp[new_state][v] = dp[state][u] + dist[u][v];
}
}
}
}
// 最终结果:遍历所有奶酪后,从任意奶酪u返回原点的最短距离
double min_total = INF;
int full_state = (1 << n) - 1;
for (int u = 0; u < n; u++) {
min_total = min(min_total, dp[full_state][u] + origin_dist[u]);
}
printf("%.2f\n", min_total);
return 0;
}