Uvalive 4043 Ants —— 二分图最大权匹配 KM算法

题目链接: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 }
View Code
原文地址:https://www.cnblogs.com/DOLFAMINGO/p/7832243.html