UVa 1659 "Help Little Laura"是一道经典的图论与动态规划结合的计算几何题目。题目描述了一个二维平面上有n个点,Laura需要从起点到终点,途中要经过所有给定的点,求最短路径长度。
这个问题的核心在于:
这道题可以转化为典型的旅行商问题(TSP)变种:
与标准TSP的区别在于:
对于n≤15的规模,我们可以采用状态压缩动态规划来解决。这是处理小规模TSP问题的经典方法。
定义dp[mask][u]表示:
状态转移方程:
dp[mask|(1<<v)][v] = min(dp[mask][u] + dist(u,v))
初始状态:
dp[1<<start][start] = 0
其他状态初始化为INF
终止条件:
当mask包含所有点时,考虑从最后一点到终点的距离
cpp复制// 计算所有点对之间的距离
vector<vector<double>> precompute_distances(const vector<Point>& points) {
int n = points.size();
vector<vector<double>> dist(n, vector<double>(n));
for(int i=0; i<n; ++i) {
for(int j=0; j<n; ++j) {
dist[i][j] = hypot(points[i].x-points[j].x, points[i].y-points[j].y);
}
}
return dist;
}
cpp复制double solve_tsp(const vector<Point>& points, int start, int end) {
int n = points.size();
auto dist = precompute_distances(points);
vector<vector<double>> dp(1<<n, vector<double>(n, INF));
// 初始化
dp[1<<start][start] = 0;
// 状态转移
for(int mask=0; mask<(1<<n); ++mask) {
for(int u=0; u<n; ++u) {
if(!(mask & (1<<u))) continue;
if(dp[mask][u] == INF) continue;
for(int v=0; v<n; ++v) {
if(mask & (1<<v)) continue;
int new_mask = mask | (1<<v);
dp[new_mask][v] = min(dp[new_mask][v], dp[mask][u] + dist[u][v]);
}
}
}
// 计算结果
double res = INF;
int full_mask = (1<<n)-1;
for(int u=0; u<n; ++u) {
res = min(res, dp[full_mask][u] + dist[u][end]);
}
return res;
}
注意:UVa题目对精度要求严格,建议:
- 使用double而非float
- 比较时使用相对误差而非绝对误差
- 输出时按要求格式化
cpp复制const double EPS = 1e-9;
bool is_equal(double a, double b) {
return fabs(a-b) < EPS;
}
cpp复制#include <bits/stdc++.h>
using namespace std;
const double INF = 1e18;
struct Point {
double x, y;
};
double solve(const vector<Point>& points, int start, int end) {
// 实现上述solve_tsp逻辑
}
int main() {
int n, case_num = 1;
while(cin >> n && n) {
vector<Point> points(n);
int start = 0, end = n-1; // 根据题目具体确定
for(int i=0; i<n; ++i) {
cin >> points[i].x >> points[i].y;
}
double ans = solve(points, start, end);
printf("Case %d: %.2lf\n", case_num++, ans);
}
return 0;
}
三点共线:
(0,0) (1,1) (2,2)
起点0,终点2
预期结果:2.828 (√8)
正方形四点:
(0,0) (0,1) (1,1) (1,0)
起点0,终点3
预期结果:3.0
cpp复制void debug_print(const vector<vector<double>>& dp) {
for(int mask=0; mask<dp.size(); ++mask) {
for(int u=0; u<dp[mask].size(); ++u) {
if(dp[mask][u] < INF/2) {
cout << "mask:" << bitset<4>(mask)
<< " u:" << u
<< " val:" << dp[mask][u] << endl;
}
}
}
}
当n>20时,状态压缩DP不再适用,可以考虑:
在实际比赛中遇到这类题目时,关键是准确识别问题模型,然后选择合适的算法框架。对于UVa1659这种规模,状态压缩DP是最稳妥的解法。