#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#define N 305
#define inf 0x3fffffff
int n,map[N][N],lx[N],ly[N],match[N];
bool s[N],t[N];//s[]、t[]为左/右第i个点是否在匈牙利树中
int min(int a,int b)
{
if(a>b)return b;
return a;
}
int max(int a,int b)
{
if(a>b)return a;
return b;
}
bool find(int u)//匈牙利,匹配(找增广路)
{
int i;
s[u]=true;
for(i=1;i<=n;i++)
{
if(!t[i]&&lx[u]+ly[i]==map[u][i])
{
t[i]=true;
if(match[i]==-1||find(match[i]))
{
match[i]=u;//u跟i匹配
return true;
}
}
}
return false;
}
int KM()
{
int ans=0;
memset(match,-1,sizeof(match));
memset(lx,0,sizeof(lx));
memset(ly,0,sizeof(ly));
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
lx[i]=max(lx[i],map[i][j]);//初始化S顶标为最大权
for(i=1;i<=n;i++)//匹配每一个点
{
while(1)
{
memset(s,false,sizeof(s));
memset(t,false,sizeof(t));
if(find(i))break;//匹配成功
else//匹配失败,找最小值
{
int a=inf;
for(int j=1;j<=n;j++)
if(s[j])//j在匈牙利树中
{
for(int k=1;k<=n;k++)
if(!t[k])//k在匈牙利树外
a=min(a,lx[j]+ly[k]-map[j][k]);//顶标修改量,使得权值减小最小
}
for(j=1;j<=n;j++)//修改顶标
{
if(s[j])lx[j]-=a;//保证至少有一条边可以加入
if(t[j])ly[j]+=a;//保证原来匹配的边修改后依然可以匹配
}
}
}
}
for(i=1;i<=n;i++)//权值相加
ans+=map[match[i]][i];
return ans;
}
int main()
{
int i,j;
while(scanf("%d",&n)!=-1)
{
for(i=1;i<=n;i++)
for(j=1;j<=n;j++)
scanf("%d",&map[i][j]);
printf("%d\n",KM());
}
return 0;
}
hdu 2255 km入门题
最新推荐文章于 2022-09-19 18:34:38 发布