题目链接:https://vjudge.net/problem/UVALive-4043
题意:
给出n个白点和n个黑点的坐标, 要求用n条不相交的线段把他们连接起来,其中每条线段恰好连接一个白点和黑点,每个点恰好连接到一条线段。输出每个白点与其相连的黑点的编号。
题解:
1.首先随便配对。然后,如果存在两对黑白点的线段是相交的,那么就交换他们的配对对象。交换之后重新形成的线段就不会相交了(画画图就可看出)。而且可以推出一个结论:交换之后,两条线段之和必定变小。这是因为根据三角形定理:
2.有了上述结论之后,我们就能推出:对于当前配对,如果线段总和还能继续变小,那么就可能存在交叉;但如果线段总和不能再小了,即已经达到最小,那么就不存在交叉了。所以我们只需要求最大权匹配(边权取反),就能满足要求了。
代码如下:
1 #include <bits/stdc++.h> 2 using namespace std; 3 typedef long long LL; 4 const int INF = 2e9; 5 const LL LNF = 9e18; 6 const double EPS = 1e-8; 7 const int MOD = 1e9+7; 8 const int MAXN = 1e2+10; 9 10 struct Node{ 11 double x, y; 12 }black[MAXN], white[MAXN]; 13 14 int nx, ny; 15 double g[MAXN][MAXN]; 16 int linker[MAXN]; 17 double lx[MAXN], ly[MAXN], slack[MAXN]; 18 bool visx[MAXN], visy[MAXN]; 19 20 bool DFS(int x) 21 { 22 visx[x] = true; 23 for(int y = 1; y<=ny; y++) 24 { 25 if(visy[y]) continue; 26 double tmp = lx[x] + ly[y] - g[x][y]; 27 if(tmp<EPS) 28 { 29 visy[y] = true; 30 if(linker[y]==-1 || DFS(linker[y])) 31 { 32 linker[y] = x; 33 return true; 34 } 35 } 36 else 37 slack[y] = min(slack[y], tmp); 38 } 39 return false; 40 } 41 42 void KM() 43 { 44 memset(linker, -1, sizeof(linker)); 45 memset(ly, 0, sizeof(ly)); 46 for(int i = 1; i<=nx; i++) 47 { 48 lx[i] = -INF; 49 for(int j = 1; j<=ny; j++) 50 lx[i] = max(lx[i], g[i][j]); 51 } 52 53 for(int x = 1; x<=nx; x++) 54 { 55 for(int i = 1; i<=ny; i++) 56 slack[i] = INF; 57 while(true) 58 { 59 memset(visx, 0, sizeof(visx)); 60 memset(visy, 0, sizeof(visy)); 61 62 if(DFS(x)) break; 63 double d = INF; 64 for(int i = 1; i<=ny; i++) 65 if(!visy[i]) 66 d = min(d, slack[i]); 67 68 for(int i = 1; i<=nx; i++) 69 if(visx[i]) 70 lx[i] -= d; 71 for(int i = 1; i<=ny; i++) 72 { 73 if(visy[i]) ly[i] += d; 74 else slack[i] -= d; 75 } 76 } 77 } 78 } 79 80 int main() 81 { 82 int kase = 0, n; 83 while(scanf("%d", &n) != EOF) 84 { 85 if(kase++) printf(" "); 86 87 nx = ny = n; 88 for (int i = 1; i <= n; i++) 89 scanf("%lf%lf", &black[i].x, &black[i].y); 90 for (int i = 1; i <= n; i++) 91 scanf("%lf%lf", &white[i].x, &white[i].y); 92 93 for (int i = 1; i <= n; i++) 94 { 95 double x1 = white[i].x, y1 = white[i].y; 96 for (int j = 1; j <= n; j++) 97 { 98 double x2 = black[j].x, y2 = black[j].y; 99 g[i][j] = -sqrt( (x1-x2)*(x1 - x2) + (y1-y2)*(y1-y2) ); 100 } 101 } 102 103 KM(); 104 for(int i = 1; i<=n; i++) 105 printf("%d ", linker[i]); 106 } 107 }