POJ 2195】 Going Home KM算法求最小权匹配模板

最大权匹配: 在一个二分图内,左顶点为X,右顶点为Y,现对于每组左右连接XiYj有权wij,求一种匹配使得所有wij的和最大。

用KM算法解决

  1. #include <iostream>  
  2. #include <cmath>  
  3. #include <vector>  
  4. #include <cstdlib>  
  5. #include <cstdio>  
  6. #include <cstring>  
  7. #include <queue>  
  8. #include <list>  
  9. #include <algorithm>  
  10. #include <map>  
  11. #include <set>  
  12. #define LL long long  
  13. #define fread() freopen("in.in","r",stdin)  
  14. #define fwrite() freopen("out.out","w",stdout)  
  15. #define Pr pair<int,int>  
  16.   
  17. using namespace std;  
  18. const int INF = 0x3f3f3f3f;  
  19. const int msz = 10000;  
  20. const double eps = 1e-8;  
  21.   
  22. //二分图  
  23. int mp[233][233];  
  24. //   x顶标   y顶标      
  25. int lx[233],ly[233],link[233],slick[233];  
  26. bool visx[233],visy[233];  
  27. // 人的坐标   房子坐标  
  28. Pr human[233],house[233];  
  29.  //人数 房数  
  30. int nm,nh;  
  31.   
  32. bool cal(int x)  
  33. {  
  34.     visx[x] = 1;  
  35.     for(int y = 0; y < nh; ++y)  
  36.     {  
  37.         if(visy[y]) continue;  
  38.   
  39.         int t = lx[x]+ly[y]-mp[x][y];  
  40.         if(t == 0)  
  41.         {  
  42.             visy[y] = 1;  
  43.             if(link[y] == -1 || cal(link[y]))  
  44.             {  
  45.                 link[y] = x;  
  46.                 return true;  
  47.             }  
  48.         }  
  49.         else slick[y] = min(slick[y],t);  
  50.     }  
  51.     return false;  
  52. }  
  53.   
  54. int KM()  
  55. {  
  56.     memset(link,-1,sizeof(link));  
  57.   
  58.     for(int i = 0; i < nm; ++i)  
  59.     {  
  60.         memset(slick,INF,sizeof(slick));  
  61.         while(1)  
  62.         {  
  63.             memset(visx,0,sizeof(visx));  
  64.             memset(visy,0,sizeof(visy));  
  65.             if(cal(i)) break;  
  66.             int d = INF;  
  67.   
  68.             for(int j = 0; j < nh; ++j)  
  69.                 if(!visy[j]) d = min(d,slick[j]);  
  70.   
  71.             for(int j = 0; j < nm; ++j)  
  72.                 if(visx[j]) lx[j] -= d;  
  73.   
  74.             for(int j = 0; j < nh; ++j)  
  75.                 if(visy[j]) ly[j] += d;  
  76.                 else slick[j] -= d;  
  77.         }  
  78.     }  
  79.   
  80.     int ans = 0;  
  81.     for(int i = 0; i < nh; ++i)  
  82.         if(link[i] != -1) ans += mp[link[i]][i];  
  83.   
  84.     return ans;  
  85. }  
  86.   
  87. int main()  
  88. {  
  89.     int n,m;  
  90.     char tmp[233];  
  91.   
  92.     while(~scanf("%d%d",&n,&m) && (n+m))  
  93.     {  
  94.         nm = nh = 0;  
  95.         for(int i = 0; i < n; ++i)   
  96.         {  
  97.             scanf("%s",tmp);  
  98.             for(int j = 0; j < m; ++j)  
  99.                 if(tmp[j] == 'm') human[nm++] = Pr(i,j);  
  100.                 else if(tmp[j] == 'H') house[nh++] = Pr(i,j);  
  101.         }  
  102.   
  103.         memset(lx,0,sizeof(lx));  
  104.         memset(ly,0,sizeof(ly));  
  105.   
  106.         for(int i = 0; i < nm; ++i)  
  107.             for(int j = 0; j < nh; ++j)  
  108.             {  
  109.                 mp[i][j] = -(abs(human[i].first-house[j].first)+abs(human[i].second-house[j].second));  
  110.                 lx[i] = max(lx[i],mp[i][j]);  
  111.             }  
  112.   
  113.         printf("%d\n",-KM());  
  114.     }  
  115.   
  116.     return 0;  
  117. }  

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值