zoukankan      html  css  js  c++  java
  • 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
  • 相关阅读:
    阿杜复博通知
    vim 配置文件 ,高亮+自动缩进+行号+折叠+优化
    网络课设总结(五)——利用CAsyncSocket进行异步通信
    网络课设总结(四)——利用CAsyncSocket进行异步通信
    网络课设总结(三)——VC++应用技巧
    ubuntu11.10下配置adsl上网
    反省
    cp命令“d”参数解释及实例
    网络课设总结(二)——VC开发环境
    SharePoint中如何获得当前用户的loginName
  • 原文地址:https://www.cnblogs.com/DOLFAMINGO/p/7832243.html
Copyright © 2011-2022 走看看