大体题意:
给你2*n 个人,每个人告诉你所处的坐标(二维坐标),要求两两配对,使得配对后距离之和最小?
思路:
纯粹的最优配对问题!
通过这个问题来复习一下 最优配对!
记忆化搜索明显递推要快!
令dp[s]表示状态s 进行两两配对的最小距离之和!
可以这么思考 :
先找到前i-1 项除了j 配对最优解,令 i 和j 进行配对!
那么只需要枚举j 即可!
那么这样的话,i 也需要枚举!
但是没必要!
求前i-1项时, i 显然是最大值!
你的状态s 已经包括了最大值!那么只需要找到状态s 的最大位即可! 剩下的j 枚举小于i 的位置!
因为从0~ 1<< n 全都能枚举到,相当于是对称的!
所以找到状态s 的最小位置 也不错!!
详细见代码:
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
const int maxn = 30;
double Min(double& a,double b){
return a > b ? b : a;
}
struct Node{
double x,y;
void read(){
scanf("%lf %lf",&x, &y);
}
}p[maxn];
double dp[1000000];
int n,ks = 0;
double dist(int i,int j){
return sqrt((p[i].x - p[j].x)*(p[i].x - p[j].x) + (p[i].y - p[j].y) * (p[i].y - p[j].y));
}
double dis[maxn][maxn];
void init(){
for (int i = 0; i < n; ++i){
for (int j = i + 1; j < n; ++j){
dis[i][j] = dis[j][i] = dist(i,j);
}
}
}
double dfs(int s){
double& ans = dp[s];
if (!s)return ans = 0;
if (ans != -1) return ans;
int i,j;
for (i = 0; i < n; ++i){
if (s & (1 << i))break;
}
ans = 1e18;
for (j = i+1; j < n; ++j){
if (s & (1 << j)){
ans = Min(ans,dis[i][j] + dfs(s ^(1 << j) ^ (1 << i)));
}
}
return ans;
}
char s[maxn];
int main(){
while(~scanf("%d",&n) && n){
n<<=1;
for (int i = 0; i < n; ++i){
scanf("%s",s);
p[i].read();
}
for (int i = 0; i < (1<<n)+1; ++i) dp[i] = -1;
init();
// printf("dist = %.2f\n",dis[0][1]);
printf("Case %d: %.2f\n",++ks,dfs((1<<n) - 1));
}
return 0;
}