POJ 2195
http://acm.pku.edu.cn/JudgeOnline/problem?id=2195
第一道二分图最佳匹配题,简单题,会二分最佳的这题也就会。就建图的时候要把距离算出来!
int n,m;
int num1,num2;
char map[150][150]; //初始输入
int mat[150][150]; //记录距离
int house[150][2]; //房子坐标
int man[150][2]; //人的坐标
int pl[150],pr[150]; //左右顶标
int visl[150],visr[150]; //是否被访问
int match[150]; //匹配
int stack[150];
int Min(int a,int b)
{
return a>b?b:a;
}
bool find(int a) // 找增广路径
{
int i;
visl[a] = 1;
for(i=0;i<num2;i++)
{
if(visr[i] == 0)
{
int val = pl[a] + pr[i] - mat[a][i];
if(val == 0)
{
visr[i] = 1;
if(match[i] == -1 || find(match[i]))
{
match[i] = a;
return 1;
}
}
else
{
if(val < stack[i])
stack[i] = val;
}
}
}
return 0;
}
int km()
{
int i,j,k;
//初始化顶标
for(i=0;i<num1;i++)
{
pl[i] = -0x7ffffff;
pr[i] = 0;
for(j=0;j<num2;j++)
{
if(mat[i][j] > pl[i])
pl[i] = mat[i][j];
}
}
memset(match,-1,sizeof(match));
for(k=0;k<num1;k++)
{
for(i=0;i<150;i++)
stack[i] = 0x7fffffff;
while(1)
{
memset(visl,0,sizeof(visl));
memset(visr,0,sizeof(visr));
if(find(k))
break;
int d = 0x7ffffff;
for(j=0;j<num2;j++)
{
if(!visr[j])
d = Min(stack[j] , d);
}
for(i=0;i<num1;i++)
{
if(visl[i])
pl[i] -= d;
if(visr[i])
pr[i] += d;
}
}
}
int sum = 0;
for(i=0;i<num1;i++)
{
sum += -1 * mat[match[i]][i];
}
return sum;
}
void Init()
{
int i,j;
num1 = 0; //房子个数
num2 = 0; //人的个数
for(i=0;i<n;i++)
{
cin>>map[i];
for(j=0;j<m;j++)
{
if(map[i][j] == 'H')
{
house[num1][0] = i;
house[num1++][1] = j;
}
if(map[i][j] == 'm')
{
man[num2][0] = i;
man[num2++][1] = j;
}
}
}
for(i=0;i<num1;i++)
{
for(j=0;j<num2;j++)
{
mat[i][j] = abs(house[i][0] - man[j][0]) + abs(house[i][1] - man[j][1]);
mat[i][j] = - mat[i][j]; //因为是取最小权值,所以取反
}
}
}
int main()
{
while(scanf("%d%d",&n,&m)!=EOF && (n!=0 || m!=0))
{
Init();
printf("%d/n",km());
}
return 0;
}