//题意:一个人匹配一个房子,使人的移动步伐之和最小
//思路:利用KM算法计算二分图最大权匹配AC 0MS,算法一如下:
代码如下:AC 63MS
//思路:利用KM算法计算二分图最大权匹配AC 0MS,算法一如下:
Code
#include <iostream>
#include <cmath>
using namespace std;
const int MAXN = 102*102/2;
const int MAX = -1u>>1;
typedef struct
{
int x,y;
}Point;
Point House[MAXN],Man[MAXN];
int numH,numM;
int map[MAXN][MAXN],N,M;
int match[MAXN],lx[MAXN],ly[MAXN];
int sx[MAXN],sy[MAXN];
char a[MAXN][MAXN];
bool path(int u)//匈牙利算法,返回最佳匹配
{
sx[u] = 1;
for( int i = 0 ; i < numM ; i ++ )
if( !sy[i] && lx[u] + ly[i] == map[u][i] )
{
sy[i] = 1;
if( match[i] == -1 || path(match[i]) )
{
match[i] = u;
return true;
}
}
return false;
}
int KM()
{
int i,j,u;
for( i = 0 ; i < numH ; i ++ )//一开始让lx最大化,ly为零,满足lx[i]+ly[j]>=weight[i][j]
{
lx[i] = -MAX;
ly[i] = 0;
for( j = 0 ; j < numM ; j ++ )
if( lx[i] < map[i][j] )
lx[i] = map[i][j];
}
memset(match,-1,sizeof(match));
for( u = 0 ; u < numH ; u ++ )//求n次增广路径
{
while( 1 )
{
memset(sx,0,sizeof(sx));//为0时未匹配,1时匹配
memset(sy,0,sizeof(sy));
if( path(u) )//寻到增广路,满足lx[i]+ly[j]=weigh[i][j],退出
break;
int dx = MAX;
for( i = 0 ; i < numM ; i ++ )//只有i匹配,j未匹配才可能找到一条更短的路
if( sx[i] )//匹配
for( j = 0 ; j < numH ; j ++ )
if( !sy[j] )//未匹配
dx = __min(lx[i]+ly[j]-map[i][j],dx);
for( i = 0 ; i < numM ; i ++ )//不断的缩小lx[i]+ly[j],最终达到lx[i]+ly[j]=weigh[i][j]
{
if( sx[i] )
lx[i] -= dx;
if( sy[i] )
ly[i] += dx;
}
}
}
int sum = 0;
for( i = 0 ; i < numH ; i ++ )
if( match[i] != -1 )
sum += map[match[i]][i];
return sum;
}
int main()
{
int i,j;
//freopen("2195.txt","r",stdin);
while( 1 )
{
scanf("%d %d",&N,&M);
if( N == 0 && M == 0 ) break;
for( i = 0 ; i < N ; i ++ )
scanf("%s",a[i]);
numH = numM = 0;
for( i = 0 ; i < N ; i ++ )
for( j = 0 ; j < M ; j ++ )
{
if( a[i][j] == 'H' )
{
House[numH].x = i;
House[numH++].y = j;
}
else if( a[i][j] == 'm' )
{
Man[numM].x = i;
Man[numM++].y = j;
}
}
for( i = 0 ; i < numH ; i ++ )//构图
for( j = 0 ; j < numM ; j ++ )
map[i][j] = -(abs(House[j].x - Man[i].x) + abs(House[j].y - Man[i].y));
int ans = KM();
printf("%d\n",-ans);
}
return 0;
}
//算法二:最小费用最大流,以SPFA实现
#include <iostream>
#include <cmath>
using namespace std;
const int MAXN = 102*102/2;
const int MAX = -1u>>1;
typedef struct
{
int x,y;
}Point;
Point House[MAXN],Man[MAXN];
int numH,numM;
int map[MAXN][MAXN],N,M;
int match[MAXN],lx[MAXN],ly[MAXN];
int sx[MAXN],sy[MAXN];
char a[MAXN][MAXN];
bool path(int u)//匈牙利算法,返回最佳匹配
{
sx[u] = 1;
for( int i = 0 ; i < numM ; i ++ )
if( !sy[i] && lx[u] + ly[i] == map[u][i] )
{
sy[i] = 1;
if( match[i] == -1 || path(match[i]) )
{
match[i] = u;
return true;
}
}
return false;
}
int KM()
{
int i,j,u;
for( i = 0 ; i < numH ; i ++ )//一开始让lx最大化,ly为零,满足lx[i]+ly[j]>=weight[i][j]
{
lx[i] = -MAX;
ly[i] = 0;
for( j = 0 ; j < numM ; j ++ )
if( lx[i] < map[i][j] )
lx[i] = map[i][j];
}
memset(match,-1,sizeof(match));
for( u = 0 ; u < numH ; u ++ )//求n次增广路径
{
while( 1 )
{
memset(sx,0,sizeof(sx));//为0时未匹配,1时匹配
memset(sy,0,sizeof(sy));
if( path(u) )//寻到增广路,满足lx[i]+ly[j]=weigh[i][j],退出
break;
int dx = MAX;
for( i = 0 ; i < numM ; i ++ )//只有i匹配,j未匹配才可能找到一条更短的路
if( sx[i] )//匹配
for( j = 0 ; j < numH ; j ++ )
if( !sy[j] )//未匹配
dx = __min(lx[i]+ly[j]-map[i][j],dx);
for( i = 0 ; i < numM ; i ++ )//不断的缩小lx[i]+ly[j],最终达到lx[i]+ly[j]=weigh[i][j]
{
if( sx[i] )
lx[i] -= dx;
if( sy[i] )
ly[i] += dx;
}
}
}
int sum = 0;
for( i = 0 ; i < numH ; i ++ )
if( match[i] != -1 )
sum += map[match[i]][i];
return sum;
}
int main()
{
int i,j;
//freopen("2195.txt","r",stdin);
while( 1 )
{
scanf("%d %d",&N,&M);
if( N == 0 && M == 0 ) break;
for( i = 0 ; i < N ; i ++ )
scanf("%s",a[i]);
numH = numM = 0;
for( i = 0 ; i < N ; i ++ )
for( j = 0 ; j < M ; j ++ )
{
if( a[i][j] == 'H' )
{
House[numH].x = i;
House[numH++].y = j;
}
else if( a[i][j] == 'm' )
{
Man[numM].x = i;
Man[numM++].y = j;
}
}
for( i = 0 ; i < numH ; i ++ )//构图
for( j = 0 ; j < numM ; j ++ )
map[i][j] = -(abs(House[j].x - Man[i].x) + abs(House[j].y - Man[i].y));
int ans = KM();
printf("%d\n",-ans);
}
return 0;
}
代码如下:AC 63MS
Code
#include <iostream>
#include <queue>
using namespace std;
const int MAXN = 205;
const int INF = -1u>>1;
int mNum,man[MAXN][2];//存储人的位置
int hNum,house[MAXN][2];//存储房子的位置
int cost[MAXN][MAXN];//cost[I][J]表示从第i个人走到第j个房子要花费几步路
int map[MAXN][MAXN];//map[i][j]表示第i个人到第j个房子有通路
int flow[MAXN][MAXN],min_flow[MAXN];
int pre[MAXN],dis[MAXN];
int hasMan[MAXN];//标志某房子里有没有住人
int N,M;
bool SPFA(int num,int s,int e)
{
queue<int>Q;
int i;
memset(pre,-1,sizeof(pre));
memset(hasMan,0,sizeof(hasMan));
for( i = 0 ; i < num ; i ++ )
dis[i] = INF;
dis[s] = 0;
Q.push(s);
hasMan[s] = 1;
min_flow[s] = INF;
while( !Q.empty() )
{
int k = Q.front();
Q.pop();
for( i = 0 ; i < num ; i ++ )
{
if( map[k][i] - flow[k][i] > 0 )
if( dis[k] + cost[k][i] < dis[i] )
{
dis[i] = dis[k] + cost[k][i];
if( !hasMan[i] )//注意题目是即使房子原先已经住人了,当还是可以站在上面
{
hasMan[i] = 1;
Q.push(i);
}
pre[i] = k;
min_flow[i] = __min(min_flow[k],map[k][i] - flow[k][i]);
}
}
hasMan[k] = 0;
}
if( pre[e] != -1 )
return true;
return false;
}
int Min_Cost_Max_Flow(int num,int s,int e)//num为节点数,s为源点,e为汇点
{
int ans = 0;
memset(flow,0,sizeof(flow));
while(SPFA(num,s,e))//寻找增广路
{
int k = e;
while( pre[k] >= 0 )
{
flow[pre[k]][k] += min_flow[e];
flow[k][pre[k]] = -flow[pre[k]][k];
k = pre[k];
}
ans += dis[e];
}
return ans;
}
int main()
{
char c[MAXN];
int i,j;
//freopen("2195.txt","r",stdin);
while( 1 )
{
scanf("%d %d",&N,&M);
if( N == 0 && M == 0 ) break;
getchar();
mNum = hNum = 0;
for( i = 1 ; i <= N ; i ++ )
{
scanf("%s",c);
for( j = 1 ; j <= M ; j ++ )
{
if( c[j-1] == 'm' )
{
man[mNum][0] = i;
man[mNum++][1] = j;
}
else if( c[j-1] == 'H' )
{
house[hNum][0] = i;
house[hNum++][1] = j;
}
}
}
//printf("%d %d\n",mNum,hNum);
memset(map,0,sizeof(map));
memset(cost,0,sizeof(cost));
for( i = 1 ; i <= mNum ; i ++ )
{
for( j = 1 ; j <= hNum ; j ++ )
{//建立人与房子的关系
cost[i][j+mNum] = abs(man[i-1][0] - house[j-1][0]) + abs(man[i-1][1] - house[j-1][1]);
cost[j+mNum][i] = -cost[i][j+mNum];
map[i][j+mNum] = 1;//注意是单向的
}
}
for( i = 1 ; i <= mNum ; i ++ )
map[0][i] = 1;//建立源点,从源点到人联通
for( j = 1 ; j <= hNum ; j ++ )
map[j+mNum][mNum+hNum+1] = 1;//建立汇点,房子与汇点联通
printf("%d\n",Min_Cost_Max_Flow(hNum+mNum+2,0,hNum+mNum+1));
}
return 0;
}
#include <iostream>
#include <queue>
using namespace std;
const int MAXN = 205;
const int INF = -1u>>1;
int mNum,man[MAXN][2];//存储人的位置
int hNum,house[MAXN][2];//存储房子的位置
int cost[MAXN][MAXN];//cost[I][J]表示从第i个人走到第j个房子要花费几步路
int map[MAXN][MAXN];//map[i][j]表示第i个人到第j个房子有通路
int flow[MAXN][MAXN],min_flow[MAXN];
int pre[MAXN],dis[MAXN];
int hasMan[MAXN];//标志某房子里有没有住人
int N,M;
bool SPFA(int num,int s,int e)
{
queue<int>Q;
int i;
memset(pre,-1,sizeof(pre));
memset(hasMan,0,sizeof(hasMan));
for( i = 0 ; i < num ; i ++ )
dis[i] = INF;
dis[s] = 0;
Q.push(s);
hasMan[s] = 1;
min_flow[s] = INF;
while( !Q.empty() )
{
int k = Q.front();
Q.pop();
for( i = 0 ; i < num ; i ++ )
{
if( map[k][i] - flow[k][i] > 0 )
if( dis[k] + cost[k][i] < dis[i] )
{
dis[i] = dis[k] + cost[k][i];
if( !hasMan[i] )//注意题目是即使房子原先已经住人了,当还是可以站在上面
{
hasMan[i] = 1;
Q.push(i);
}
pre[i] = k;
min_flow[i] = __min(min_flow[k],map[k][i] - flow[k][i]);
}
}
hasMan[k] = 0;
}
if( pre[e] != -1 )
return true;
return false;
}
int Min_Cost_Max_Flow(int num,int s,int e)//num为节点数,s为源点,e为汇点
{
int ans = 0;
memset(flow,0,sizeof(flow));
while(SPFA(num,s,e))//寻找增广路
{
int k = e;
while( pre[k] >= 0 )
{
flow[pre[k]][k] += min_flow[e];
flow[k][pre[k]] = -flow[pre[k]][k];
k = pre[k];
}
ans += dis[e];
}
return ans;
}
int main()
{
char c[MAXN];
int i,j;
//freopen("2195.txt","r",stdin);
while( 1 )
{
scanf("%d %d",&N,&M);
if( N == 0 && M == 0 ) break;
getchar();
mNum = hNum = 0;
for( i = 1 ; i <= N ; i ++ )
{
scanf("%s",c);
for( j = 1 ; j <= M ; j ++ )
{
if( c[j-1] == 'm' )
{
man[mNum][0] = i;
man[mNum++][1] = j;
}
else if( c[j-1] == 'H' )
{
house[hNum][0] = i;
house[hNum++][1] = j;
}
}
}
//printf("%d %d\n",mNum,hNum);
memset(map,0,sizeof(map));
memset(cost,0,sizeof(cost));
for( i = 1 ; i <= mNum ; i ++ )
{
for( j = 1 ; j <= hNum ; j ++ )
{//建立人与房子的关系
cost[i][j+mNum] = abs(man[i-1][0] - house[j-1][0]) + abs(man[i-1][1] - house[j-1][1]);
cost[j+mNum][i] = -cost[i][j+mNum];
map[i][j+mNum] = 1;//注意是单向的
}
}
for( i = 1 ; i <= mNum ; i ++ )
map[0][i] = 1;//建立源点,从源点到人联通
for( j = 1 ; j <= hNum ; j ++ )
map[j+mNum][mNum+hNum+1] = 1;//建立汇点,房子与汇点联通
printf("%d\n",Min_Cost_Max_Flow(hNum+mNum+2,0,hNum+mNum+1));
}
return 0;
}