poj 2195 Going Home (km算法)

题目链接:

  http://poj.org/problem?id=2195

解题思路:

  把man和home都提取出来,然后算出每个man和home的距离算出来,然后建立匹配图,套用km算法的模板,求最小权值匹配,km模板一般是求最大权匹配,求最小的话,一般是取负,当然如果感觉取负逼格太low,也可以用下面的办法,改进模板。

代码:

  1 //写的代码太菜,不懂建立匹配图的时候为什么下表从零开始就会使劲wa,但是改成1就会ac,有看出来的小伙伴们请大声说出来,跪谢!!!!!
  2 //KM算法求完备匹配下的最小权匹配
  3 #include <cstdio>
  4 #include <cstring>
  5 #include <iostream>
  6 #include <algorithm>
  7 #include <cmath>
  8 using namespace std;
  9 
 10 #define maxn 110
 11 #define INF 0x3f3f3f3f
 12 int map[maxn][maxn]/*匹配图*/;
 13 int used[maxn]/*x与y的匹配值*/;
 14 int  s[maxn]/*修改量*/;
 15 int n, m;
 16 int lx[maxn], ly[maxn];/*顶标*/
 17 int visx[maxn], visy[maxn];
 18 
 19 struct node
 20 {
 21     int x, y;
 22     void init(int xx, int yy)
 23     {
 24         x = xx;
 25         y = yy;
 26     }
 27 };
 28 
 29 int Fas(int x, int y)
 30 {
 31     if (x > y)
 32         return x - y;
 33     return y - x;
 34 }
 35 int find (int x)
 36 {//匈牙利算法,增广路经,扩大相等子图
 37     visx[x] = 1;
 38     for (int i=1; i<n; i++)
 39     {
 40         if (!visy[i] && lx[x]+ly[i] == map[x][i])
 41         {
 42             visy[i] = 1;
 43             if (!used[i] || find(used[i]))
 44             {
 45                 used[i] = x;
 46                 return 1;
 47             }
 48         }
 49         else
 50             //s[i] = min (s[i], lx[x] + ly[i] - map[x][i]);
 51             //最大权匹配
 52             s[i] = min (s[i], map[x][i] - (lx[x] + ly[i]));//更新修改值,保证最小,使求得的结果最优
 53     }
 54     return 0;
 55 }
 56 int KM()
 57 {
 58     memset (used, 0, sizeof(used));
 59     memset (ly, 0, sizeof(ly));
 60     for (int i=1; i<n; i++)//初始化顶标
 61         lx[i] = INF;//lx[i] = 0;最大权匹配
 62 
 63     for (int i=1; i<n; i++)
 64         for (int j=0; j<n; j++)
 65             lx[i] = min(lx[i], map[i][j]);
 66 
 67     for (int i=1; i<n; i++)
 68     {
 69         for (int j=1; j<n; j++)
 70             s[j] = INF;
 71         while (1)
 72         {
 73             memset (visx, 0, sizeof(visx));
 74             memset (visy, 0, sizeof(visy));
 75 
 76             if (find(i))
 77                 break;
 78 
 79             int num = INF;
 80             for (int j=1; j<n; j++)
 81                 if (!visy[j])//
 82                 num = min (num, s[j]);
 83 
 84             for (int j=1; j<n; j++)
 85             {
 86                 if (visx[j])
 87                     lx[j] += num;//lx[j] -= num;最大权匹配
 88                 if (visy[j])
 89                     ly[j] -= num;//ly[j] += num;最大权匹配
 90                 else
 91                     s[j] -= num;
 92             }
 93         }
 94     }
 95     int res = 0;
 96     for (int i=1; i<n; i++)
 97         res += map[used[i]][i];
 98     return res;
 99 
100 }
101 int main ()
102 {
103     char str[maxn];
104     int a, b;
105     node home[maxn], man[maxn];
106     while (scanf ("%d %d", &a, &b), a+b)
107     {
108         n = m = 1;
109        memset (map, 0, sizeof(map));
110 
111         for (int i=0; i<a; i++)
112         {
113             scanf ("%s", str);
114             for (int j=0; str[j]; j++)
115             {
116                 if (str[j] == 'H')
117                     home[n++].init(i, j);
118                 if (str[j] == 'm')
119                     man[m++].init(i, j);
120             }
121         }
122         for (int i=1; i<n; i++)
123             for (int j=1; j<m; j++)
124             map[i][j] = Fas(home[i].x , man[j].x) + Fas(home[i].y , man[j].y);
125         printf ("%d\n", KM());
126     }
127     return 0;
128 }

 

转载于:https://www.cnblogs.com/alihenaixiao/p/4479780.html

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值