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;
}
#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;
}