题意:给你n * 2 个点,要求将这些点两两配对,是的所有点中两点的距离之和最小。
思路:集合上的DP,最优配对问题。
d(i, S)表示把前i个点中,位于集合S中的元素两俩配对的最小距离和,d(i, S) = min{|Pi Pj| + d(i - 1,S - {i} - {j})},(j属于S)
但其实阶段i可以不用保存,因为它已经隐含在S中了——S中的最大元素就是i.
所以可以转化为一维,即d(S)表示把S中的元素两两配对的最小距离和,d(S) = min{|Pi Pj| + d(S - {i} - {j})},(j属于S, i = max{S});
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
const int INF = 0x3f3f3f3f;
const int MAXN = 1 << 21;
struct node{
int x, y;
}t[MAXN];
int n;
char name[MAXN];
double d[MAXN];
double dis(int a, int b) {
return sqrt((t[a].x - t[b].x) * (t[a].x - t[b].x) + (t[a].y - t[b].y) * (t[a].y - t[b].y));
}
int main() {
int cas = 1;
while (scanf("%d", &n) && n) {
int num = n * 2;
for (int i = 0; i < num; i++)
scanf("%s %d %d", name, &t[i].x, &t[i].y);
d[0] = 0;
for (int S = 1; S < (1 << num); S++) {
int i, j;
d[S] = INF;
for (i = 0; i < num; i++)
if (S & (1 << i))
break;
for (j = i + 1; j < num; j++)
if (S & (1 << j))
d[S] = min(d[S], d[S ^ (1 << i) ^ (1 << j)] + dis(i, j));
}
printf("Case %d: %.2lf\n", cas++, d[(1 << num) - 1]);
}
return 0;
}