pku 2195 Going Home KM最小权匹配问题

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

在一个n*m的方格里有nx人(m)和ny个房子(H),(nx = ny)人每次可以向四周移动单位距离,花费1¥,求最小花费是每个人都能进入一个房间。

最小费用最大流可以做http://www.cnblogs.com/E-star/archive/2012/06/28/2567079.html

这里是个二分图求最小权匹配问题,可以用KM算法求,只要把w[i][j] 化成负数,太用木板求最大的负数,然后输出-KM()就得到最小的整数了。

#include <cstdio>
#include <cstring>
#include <iostream>
#include <cstdlib>
#define maxn 102
using namespace std;

struct node
{
    int x,y;
}p[maxn*50],h[maxn*50];
const int inf = 99999999;
int w[maxn][maxn],link[maxn];
int lx[maxn],ly[maxn];//记录顶标
int slack[maxn];
bool vtx[maxn],vty[maxn];//记录X,Y点集是否被访问过
int nx,ny,n,m;
char str[maxn][maxn];

bool dfs(int i)
{
    int j;
    vtx[i] = true;
    for (j = 0; j < ny; ++j)
    {
        if (vty[j]) continue;
        int tmp = lx[i] + ly[j] - w[i][j];
        if (tmp == 0)
        {
            vty[j] = true;
            if (link[j] == -1 || dfs(link[j]))
            {
                link[j] = i;
                return true;
            }
        }
        else
        slack[j] = min(tmp,slack[j]);
    }
    return false;
}
int KM()
{
    int i,j;
    for (i = 0; i < nx; ++i)
    {
        for (j = 0,lx[i] = -inf; j < ny; ++j)
        {
            lx[i] = max(lx[i],w[i][j]);
        }
    }
    for (i = 0; i < maxn; ++i)
    {
        link[i] = -1; ly[i] = 0;
    }
    for (i = 0; i < nx; ++i)
    {
        for (j = 0; j < ny; ++j) slack[j] = inf;
        while (1)
        {
            for (j = 0; j < maxn; ++j) vtx[j] = vty[j] = false;
            if (dfs(i)) break;
            int d = inf;
            for (j = 0; j < ny; ++j)
            {
                if (!vty[j] && d > slack[j])
                d = slack[j];
            }
            if (d == inf) return -1;
            for (j = 0; j < nx; ++j)
            if (vtx[j]) lx[j] -= d;
            for (j = 0; j < ny; ++j)
            if (vty[j]) ly[j] += d;
            else slack[j] -= d;
        }
    }
    int sum = 0;
    for (i = 0; i < ny; ++i)
    {
        if (link[i] > -1) sum += w[link[i]][i];
    }
    return sum;
}
int main()
{
    int i,j;
    while (~scanf("%d%d",&n,&m))
    {
        if (!n && !m) break;
        nx = ny = 0;
        for (i = 0; i < n; ++i)
        {
            scanf("%s",str[i]);
            for (j = 0; j < m; ++j)
            {
                if (str[i][j] == 'm')
                {
                    p[nx].x = i; p[nx++].y = j;
                }
                else if (str[i][j] == 'H')
                {
                    h[ny].x = i; h[ny++].y = j;
                }
            }
        }
        for (i = 0; i < nx; ++i)
        {
            for (j = 0; j < ny; ++j)
            {
                w[i][j] = -inf;
            }
        }
        for (i = 0; i < nx; ++i)
        {
            for (j = 0; j < ny; ++j)
            {
                w[i][j] = -(abs(p[i].x - h[j].x) + abs(p[i].y - h[j].y));
            }
        }

        printf("%d\n",-KM());
    }
    return 0;
}

  

posted @ 2012-07-21 20:58  E_star  阅读(263)  评论(0编辑  收藏  举报