1、poj 2195 Going Home(二分图最小权匹配)

  题意:图上有n个房子,n个人,现在安排每个人回到一所房间,求最小的步数和。

  思路:KM算法模板题。注意反向。

  附:推荐km算法大神博客:

    http://blog.sina.com.cn/s/blog_691ce2b701016reh.html

    http://www.cnblogs.com/wenruo/p/5264235.html

    http://blog.csdn.net/sixdaycoder/article/details/47720471

    http://blog.csdn.net/zxn0803/article/details/49999267

    http://www.cnblogs.com/Lanly/p/6291214.html

  1 #include <cstdio>
  2 #include <cstring>
  3 #include <algorithm>
  4 using namespace std;
  5 #define N 105
  6 #define INF 0x3f3f3f
  7 char maze[N][N];
  8 int mp[N][N], match[N], lx[N], ly[N], visx[N], visy[N], slack[N];
  9 int n, m,ny,nx;
 10 //lx,ly为顶标,nx,ny分别为x点集y点集的个数
 11 //match数组记录右边y端点所连的左端点x,visx,visy数组记录是否曾访问过,也是判断是否在增广路上
 12 struct node
 13 {
 14     int a, b;
 15 }sa[N], sb[N];
 16 //KM求二分图最小匹配模板:只需把权值都变成负的,再用KM算出最大权匹配,然后取反就是答案
 17 //学习KM地址http://blog.sina.com.cn/s/blog_691ce2b701016reh.html
 18 bool dfs(int x)
 19 {
 20     visx[x] = 1;
 21     for (int y = 1; y <= ny; y++)
 22     {
 23         if (visy[y]) continue;
 24         int t = lx[x] + ly[y] - mp[x][y];
 25         if (t == 0)//(x,y)在相等子图中
 26         {
 27             visy[y] = 1;
 28             if (match[y] == -1 || dfs(match[y]))
 29             {//注意这里要搜match[y]而不是y,因为我们只搜索x侧的,不需要搜索y侧的 
 30                 match[y] = x;
 31                 return true;
 32             }
 33         }
 34         else if (slack[y]>t) slack[y] = t;//(x,y)不在相等子图中且y不在交错树中slack 取最小的
 35     }
 36     return false;
 37 }
 38 
 39 int KM()
 40 {
 41     memset(match, -1, sizeof(match));
 42     memset(lx, -INF, sizeof(lx));
 43     memset(ly, 0, sizeof(ly));
 44     for (int i = 1; i <= nx; i++)
 45     {
 46         for (int j = 1; j <= ny; j++)
 47         {//lx初始化为与它关联边中最大的
 48             if (mp[i][j]>lx[i]) lx[i] = mp[i][j];
 49         }
 50     }
 51     for (int x = 1; x <= nx; x++)
 52     {
 53         for (int y = 1; y <= ny; y++)
 54             slack[y] = INF; //每次换新的x结点都要初始化slack
 55         while (1)
 56         {
 57             memset(visx, 0, sizeof(visx));
 58             memset(visy, 0, sizeof(visy));//这两个初始化必须放在这里,因此每次dfs()都要更新
 59             if (dfs(x)) break;
 60             //若成功(找到了增广轨),则该点增广完成,进入下一个点的增广
 61             //若失败(没有找到增广轨),则需要改变一些点的标号,使得图中可行边的数量增加。方法为:将所有在增广轨中(就是在增广过程中遍历到)的X方点的标号全部减去一个常数d,所有在增广轨中的Y方点的标号全部加上一个常数d.
 62             int d = INF;
 63             for (int y = 1; y <= ny; y++)
 64             {
 65                 if (!visy[y] && d>slack[y]) d = slack[y];
 66             }
 67             for (int x = 1; x <= nx; x++)
 68             {
 69                 if (visx[x]) lx[x] -= d;
 70             }
 71             for (int y = 1; y <= ny; y++)
 72             {//修改顶标后,要把所有不在交错树中的Y顶点的slack值都减去d,这是因为lx[i] 减小了delta,slack[j] = min(lx[i] + ly[j] -w[i][j]) --j不属于交错树--也需要减少delta,第二类边
 73                 if (visy[y]) ly[y] += d;
 74                 else slack[y] -= d;
 75             }
 76         }
 77     }
 78     int res = 0;
 79     for (int i = 1; i <= ny; i++)
 80     {
 81         if (match[i]>-1) res += mp[match[i]][i];
 82     }
 83     return res;
 84 }
 85 
 86 int main()
 87 {
 88     int n, m;
 89     while (~scanf("%d%d", &n, &m))
 90     {
 91         if (n + m == 0) break;
 92         for (int i = 1; i <= n; i++)
 93         {
 94             scanf("%s", maze[i] + 1);
 95         }
 96         int cnt1 = 0, cnt2 = 0;
 97         for (int i = 1; i <= n; i++)
 98         {
 99             for (int j = 1; j <= m; j++)
100             {
101                 if (maze[i][j] == 'm')
102                 {
103                     sa[++cnt1].a = i;
104                     sa[cnt1].b = j;
105                 }
106                 if (maze[i][j] == 'H')
107                 {
108                     sb[++cnt2].a = i;
109                     sb[cnt2].b = j;
110                 }
111             }
112         }
113         int cnt = cnt1;
114         for (int i = 1; i <= cnt1; i++)
115         {
116             for (int j = 1; j <= cnt2; j++)
117             {
118                 mp[i][j] = abs(sa[i].a - sb[j].a) + abs(sa[i].b - sb[j].b);
119                 mp[i][j] = -mp[i][j];//取反求最大权匹配(也可以用一个极大值减去原来的值求最大权匹配)
120             }
121         }
122         ny = nx = cnt;
123         printf("%d\n", -KM());//再取反则为最小权
124     }
125     return 0;
126 }
View Code

相关文章:

  • 2022-12-23
  • 2021-08-23
  • 2021-10-10
  • 2022-12-23
  • 2022-01-01
  • 2022-12-23
猜你喜欢
  • 2022-12-23
  • 2022-12-23
  • 2021-10-24
  • 2021-12-10
  • 2021-06-29
  • 2021-12-31
相关资源
相似解决方案