最优配对问题
jzoj 3420
题目大意:
在平面上有n个点,现在要把他们拼成n/2对,拼接两个点的代价是他们的平面距离,现在问代价总和最小是多少
输入样例
4
8730 9323
-3374 3929
-7890 -6727
1257 4689
输出样例
20366.60
数据范围
2<=N<=20
解题思路#1:
用dfs每一次选1个数和当前数字匹配,如果当前数字选过了,就进入下一层
代码#1:
#include<cmath>
#include<cstdio>
#define min(a,b) (a)<(b)?(a):(b)
using namespace std;
int n,p[30];
double ans,x[30],y[30];
double dis(int a,int b){return sqrt((x[a]-x[b])*(x[a]-x[b])+(y[a]-y[b])*(y[a]-y[b]));}//计算
void dfs(int dep,double sum)
{if (dep>n){ans=min(ans,sum);//尝试更新答案return;}if (sum>=ans) return;//无法更新答案了if (p[dep]) dfs(dep+1,sum);//选过就直接下一层else{for (int i=dep+1;i<=n;++i)if (!p[i]){p[i]=1;dfs(dep+1,sum+dis(dep,i));//选一个和它匹配的数p[i]=0;}}
}
int main()
{scanf("%d",&n);for (int i=1;i<=n;++i)scanf ("%lf %lf",&x[i],&y[i]);ans=9999999999.99;dfs(1,0.0);printf("%.2lf",ans);
}
解题思路#2:
用bfs先造出一个状压模型,然后用模型进行状压DP
代码#2:
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#define min(a,b) (a)<(b)?(a):(b)
using namespace std;
int n,tail=1,q[1<<21];
double x[30],y[30],f[1<<21];
int pd(int x,int y){return x&(1<<y);}
double dis(int a,int b){return sqrt((x[a]-x[b])*(x[a]-x[b])+(y[a]-y[b])*(y[a]-y[b]));}
void bfs()//制造模型
{q[1]=0;int h,head=0;while(head<tail){h=q[++head];int i=n-1;for (;i>=0;--i)if (pd(h,i)) break;//找一个最高位的1for (int j=n-1;j>i;--j)//在最高位后面加1q[++tail]=h|(1<<j);}
}
void dp()
{for (int i=1;i<=tail;++i){int s=q[i];int j=n-1;for (;j>=0;--j)if (!pd(s,j)) break;//找最高位的0for (int k=0;k<j;++k)//找一个和它相匹配的数if (!pd(s,k))f[s|(1<<j)|(1<<k)]=min(f[s|(1<<j)|(1<<k)],f[s]+dis(j,k));//状压DP}
}
int main()
{scanf("%d",&n);for (int i=0;i<n;++i)scanf("%lf %lf",&x[i],&y[i]);memset(f,0x7f,sizeof(f));f[0]=0;bfs();dp();printf("%.2lf",f[(1<<n)-1]);//输出
}