POJ-3565 Ants---KM算法+slack优化

题目链接:

https://vjudge.net/problem/POJ-3565

题目大意:

在坐标系中有N只蚂蚁,N棵苹果树,给你蚂蚁和苹果树的坐标。让每只蚂蚁去一棵苹果树,

一棵苹果树对应一只蚂蚁。这样就有N条直线路线,问:怎样分配,才能使总路程和最小,且

N条线不相交。

解题思路:

用一个图来说明思路。

假设A、B为蚂蚁,C、D为苹果树。则存在两种匹配:第一种是AD、BC,第二种是AC、BD。

根据三角形不等式AD+BC < AC+BD,最后得到很重要的一个性质——满足总路程之和最小

的方案一定不相交。现在来构建二分图,一边为蚂蚁,另一边为苹果树,以距离为边权值,题

目就变为了求带权二分图最小权和的最佳匹配。反向来思考,将距离乘以-1取负值建图,那么

就变为了求带权二分图最大权和的最佳匹配。直接用KM算法来做。

注意:用double,用slack数组优化(比全局变量快很多,网上看到说对于完全图优化会很快,用全局变量代替slack数组就超时)

  1 #include<iostream>
  2 #include<vector>
  3 #include<cstring>
  4 #include<queue>
  5 #include<cmath>
  6 #include<cstdio>
  7 using namespace std;
  8 const int maxn = 300 + 10;
  9 const int INF = 0x3f3f3f3f;
 10 double Map[maxn][maxn];
 11 double wx[maxn], wy[maxn];//顶标值
 12 int cx[maxn], cy[maxn];
 13 //cx[i]表示与x部的点i匹配的y部的点的编号
 14 //cy[i]表示与y部的点i匹配的x部的点的编号
 15 bool visx[maxn], visy[maxn];//标记y部的点是否加入增广路
 16 int cntx, cnty;//x部点的数目和y部点的数目
 17 double minz;//边权顶标最小值
 18 double slack[maxn];
 19 bool dfs(int u)//进入的都是X部的点
 20 {
 21     visx[u] = 1;
 22     for(int v = 1; v <= cnty; v++)
 23     {
 24         if(!visy[v] && Map[u][v] != INF)
 25         {
 26             double t = wx[u] + wy[v] - Map[u][v];
 27             if(fabs(t) < 1e-5)
 28             {
 29                 visy[v] = 1;
 30                 if(cy[v] == -1 || dfs(cy[v]))
 31                 {
 32                     cx[u] = v;
 33                     cy[v] = u;
 34                     return true;
 35                 }
 36             }
 37             else if(slack[v] > t)
 38                 slack[v] = t;
 39         }
 40     }
 41     return false;
 42 }
 43 
 44 void KM()
 45 {
 46     memset(cx, -1, sizeof(cx));
 47     memset(cy, -1, sizeof(cy));
 48     memset(wy, 0, sizeof(wy));
 49     for(int i = 0; i <= cntx; i++)wx[i] = -1.0 * INF;
 50     for(int i = 1; i <= cntx; i++)
 51     {
 52         for(int j = 1; j <= cnty; j++)
 53         {
 54             wx[i] = max(wx[i], Map[i][j]);
 55         }
 56     }
 57     for(int i = 1; i <= cntx; i++)
 58     {
 59         for(int j = 1; j <= cnty; j++)
 60             slack[j] = INF;
 61         while(1)
 62         {
 63             minz = 1.0 * INF;
 64             memset(visx, 0, sizeof(visx));
 65             memset(visy, 0, sizeof(visy));
 66             if(dfs(i))break;
 67             double d = INF;
 68             for(int i = 1; i <= cnty; i++)
 69                 if(!visy[i] && d > slack[i])
 70                 d = slack[i];
 71             for(int j = 1; j <= cntx; j++)
 72                 if(visx[j])wx[j] -= d;
 73             for(int j = 1; j <= cnty; j++)
 74                 if(visy[j])wy[j] += d;
 75                 else slack[j] -= d;
 76         }
 77     }
 78     for(int i = 1; i <= cntx; i++)
 79         printf("%d\n", cx[i]);
 80 }
 81 struct node
 82 {
 83     double x, y;
 84 }a[maxn], b[maxn];
 85 double dis(node a, node b)
 86 {
 87     return -sqrt((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y));
 88 }
 89 int main()
 90 {
 91     int n;
 92     scanf("%d", &n);
 93     cntx = cnty = n;
 94     for(int i = 1; i <= n; i++)scanf("%lf%lf", &a[i].x, &a[i].y);
 95     for(int i = 1; i <= n; i++)scanf("%lf%lf", &b[i].x, &b[i].y);
 96     for(int i = 1; i <= n; i++)
 97     {
 98         for(int j = 1; j <= n; j++)
 99             Map[i][j] = dis(a[i], b[j]);
100     }
101     KM();
102 }

 

posted @ 2018-04-18 01:34  _努力努力再努力x  阅读(780)  评论(0编辑  收藏  举报