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;

}

  

你可能感兴趣的:(home)