span style="color:#E8A02B"

PKU 2195:KM算法求最小权二分匹配,模板题,构图很简单,直接把人当作左边的点,房子当作右边的点,两者之间的曼哈顿距离当作权值即可。第一次搞带权二分匹配的题,就是用KM算法求最小权的时候要加个处,由于KM求的是最大权,所以在套模板之前把权值都取下相反值最后再把KM算法求出来的最大权值取反即可。

#include <stdio.h>
#include <memory.h>

#define ABS(a) ( (a) > 0 ? (a) : -(a) )
#define MAXN 110

int n, m, numh, numm;
char map[MAXN][MAXN];
int xm[MAXN], ym[MAXN], xh[MAXN], yh[MAXN];
bool visitedx[MAXN], visitedy[MAXN];
int match[MAXN], w[MAXN][MAXN], N;//match[]存放的右顶点的匹配信息,w[][]存放的是权值,N是右顶点数
int lx[MAXN], ly[MAXN], slack[MAXN];//lx[],ly[]分别存放的是左右顶标的信息,slack[]是松弛量

bool dfs( int x )
{
    visitedx[x] = true;
    for(int y=0; y<numm; ++y)
    {
        if( !visitedy[y] )
        {
            int tmp = lx[x] + ly[y] - w[x][y];
            if( tmp == 0 )
            {
                visitedy[y] = true;
                if( match[y] == -1 || dfs(match[y]) )
                {
                    match[y] = x;
                    return true;
                }
            }
            else
            {
                if( slack[y] > tmp ) slack[y] = tmp;
            }
        }
    }
    return false;
}

void KM()
{
    //match[]中存储的是y的匹配信息!
    memset(match, -1, sizeof(match));
    int i, j, k;
    //初始化顶标
    for(i=0; i<numh; ++i)
    {
        lx[i] = 0x80000000;
        for(j=0; j<numm; ++j)
        {
            if( lx[i] < w[i][j] ) lx[i] = w[i][j];
        }
    }
    memset(ly, 0, sizeof(ly));
    for(int x=0; x<numh; ++x)
    {
        for(j=0; j<numm; ++j)
        {
            slack[j] = 0x7fffffff;
        }
        while( true )
        {
            memset(visitedx, 0, sizeof(visitedx));
            memset(visitedy, 0, sizeof(visitedy));
            if( dfs(x) ) break;
            int min = 0x7fffffff;
            for(i=0; i<numm; ++i)
            {
                if( !visitedy[i] && min > slack[i] ) min = slack[i];
            }
            for(i=0; i<numh; ++i)
            {
                if( visitedx[i] ) lx[i] -= min;
            }
            for(i=0; i<numm; ++i)
            {
                if( visitedy[i] ) ly[i] += min;
                else slack[i] -= min;
            }
        }
    }
}

int main()
{
    while ( scanf("%d %d", &n, &m), n && m )
    {
        int i, j;
        numm = numh = 0;
        for(i=0; i<n; ++i)
        {
            scanf("%s", map[i]);
        }
        for(i=0; i<n; ++i)
        {
            for(j=0; j<m; ++j)
            {
                if( map[i][j] == 'H' )
                {
                    xh[numh] = i;
                    yh[numh++] = j;
                }
                else if( map[i][j] == 'm' )
                {
                    xm[numm] = i;
                    ym[numm++] = j;
                }
            }
        }
        for(i=0; i<numh; ++i)
        {
            for(j=0; j<numm; ++j)
            {
                w[i][j] = -( ABS( xh[i] - xm[j] ) + ABS( yh[i] - ym[j]) );
            }
        }
        KM();
        int ans = 0;
        for(i=0; i<numm; ++i)
        {
            ans += w[ match[i] ][i];
        }
        printf("%d\n", -ans);
    }
    return 0;
}

posted on 2012-01-26 16:51  青色有角三倍速  阅读(193)  评论(0编辑  收藏  举报