Problem Description
一个棋盘,有n个黑点和n个白点,求一种方案让黑白点两两配对且任意一对黑白点之间的连线不相交
思路:因为所有线段不相交,所以每条线段长度之和最小,因为必然有一种方案,所以是完全匹配,把点点间距离取反,跑一个KM最大带权匹配就好了
#include<iostream> #include<cstring> #include<cmath> using namespace std; const int N = 220; const double eps = 1e-6; const int INF = 1e9; double g[N][N], la[N], lb[N], delta; bool va[N], vb[N]; int n,mat[N],ans[N]; double dist(int x1,int y1,int x2,int y2){ int tx = x1 - x2, ty = y1 - y2; return -sqrt(tx * tx + ty * ty); } bool che(double x){ return fabs(x) < eps;} bool dfs(int x){ va[x] = 1; for(int y = 1; y <= n; y++){ if(!vb[y]) if(che(la[x] + lb[y] - g[x][y])){ vb[y] = 1; if(!mat[y] || dfs(mat[y])){ mat[y] = x; return 1; } } } return 0; } void update() { delta = 1e10; for (int i = 1; i <= n; i ++) if (va[i]) { for (int j = 1; j <= n; j ++) if (!vb[j]) { delta = min(delta, la[i] + lb[j] - g[i][j]); } } for (int i = 1; i <= n; i ++) { la[i] -= va[i] ? delta : 0; lb[i] += vb[i] ? delta : 0; } } void KM(){ for(int i = 1; i <= n; i++){ la[i] = -INF; lb[i] = 0; for(int j = 1; j <= n; j++) la[i] = max(la[i],g[i][j]); } for(int i = 1; i <= n; i++) while(1){ memset(va,0,sizeof(va)); memset(vb,0,sizeof(vb)); if(dfs(i)) break; update(); } return ; } void init(){ memset(g,0,sizeof(g)); memset(la,0,sizeof(la)); memset(lb,0,sizeof(lb)); memset(ans,0,sizeof(ans)); } int main(){ ios::sync_with_stdio(false); double x[110], y[110]; while(cin>>n){ init(); for(int i = 1; i <= n; i++) cin>>x[i]>>y[i]; double xx,yy; for(int i = 1; i <= n; i++){ cin>>xx>>yy; for(int j = 1; j <= n; j++) g[j][i] = dist(x[j],y[j],xx,yy); } KM(); for(int i = 1; i <= n; i++) ans[mat[i]] = i; for(int i = 1; i <= n; i++) cout<<ans[i]<<endl; } return 0; }