您的位置:首页 > 编程语言 > Go语言

POJ_2195_Going Home

2015-08-28 21:37 477 查看
题意:用'H','m','.'作出矩阵,'H'代表房子,'m'代表人,人一次只能水平或者垂直移动到相邻的点,问所有人一共走的步数的最小值。

分析:明显的求二分图最大权匹配。KM算法求得的是最大权匹配,而题中要求的是最小值,所以要将边的权值以其负值储存。

  有一点需要注意:link数组(匹配数组)必须初始化为-1,如果初始化为0,则link[0]=0,则默认第0个人与第0个房子匹配,在执行匈牙利算法是就会出错,找到错误的增广路,第一次提交就错在这。

总结:基本能只用KM算法,熟练度还有待增强。

代码:

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<stdlib.h>
using namespace std;
#define Del(x,y) memset(x,y,sizeof(x))
#define N 105

struct Point
{
int x,y;
};
Point house
;
Point man
;

int n,m;
int cnth,cntm;
int lx
,ly
,link
,w

;
bool S
,T
;
char grid
;

int dis(Point a,Point b)
{
return (abs(a.x-b.x)+abs(a.y-b.y));
}
void update()
{
int a=99999999;
for(int i=0; i<cnth; i++)
if(S[i])
for(int j=0; j<cntm; j++)
if(!T[j])
a=min(a,lx[i]+ly[j]-w[i][j]);
for(int i=0; i<cnth; i++)
{
if(S[i])
lx[i]-=a;
if(T[i])
ly[i]+=a;
}
}

bool match(int i)
{
S[i]=true;
for(int j=0; j<cntm; j++)
if(lx[i]+ly[j]-w[i][j]==0&&!T[j])
{
T[j]=true;
if(link[j]==-1||match(link[j]))
{
link[j]=i;
return true;
}
}
return false;
}

void KM()
{
Del(link,-1);    ///必须初始化为-1,如果初始化为0,则link[0]=0,则默认第0个人与第0个房子匹配,在执行匈牙利算法是就会出错,找到错误的增广路
for(int i=0; i<cnth; i++)
{
lx[i]=ly[i]=0;
for(int j=0; j<cntm; j++)
lx[i]=max(lx[i],w[i][j]);
}
for(int i=0; i<cnth; i++)
for(;;)
{
Del(S,0);
Del(T,0);
if(match(i))
break;
else
update();
}
}

int main()
{
while(~scanf("%d%d",&n,&m)&&n!=0)
{
cntm=cnth=0;
for(int i=0; i<n; i++)
{
scanf("%s",grid);
for(int j=0; j<m; j++)
{
if(grid[j]=='H')
{
house[cnth].x=i;
house[cnth].y=j;
cnth++;
}
if(grid[j]=='m')
{
man[cntm].x=i;
man[cntm].y=j;
cntm++;
}
}
}
for(int i=0; i<cnth; i++)
for(int j=0; j<cntm; j++)
w[i][j]=-dis(house[i],man[j]);
KM();
int ans=0;
for(int i=0; i<cntm; i++)
ans+=dis(house[link[i]],man[i]);
printf("%d\n",ans);
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: