题意:有n个人匹配n个房子,对于每一个匹配有一个权值,问最大带权二分图匹配。
KM算法的模版,我是学的这个blog
事实上费用流也是资磁的,因为图太过于稠密会T掉。可以st连向人,流量为1,费用为0,每对关系连边,流量为1,费用为权值,房子连向ed,流量为1,费用为0,然后跑费用流即可,这种费用流的建图方式也是最大(带权)二分图匹配的通常建图方式。
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
const int maxn=310;
const int INF=99999999;
int map[maxn][maxn];
int exg[maxn],exb[maxn];
bool visg[maxn],visb[maxn];
int match[maxn];
int need[maxn];
int n;
bool find_miu(int x)
{
visg[x]=true;
for(int y=1;y<=n;y++)
{
if(visb[y]) continue;
int gap=exg[x]+exb[y]-map[x][y];
if(gap==0)
{
visb[y]=true;
if(match[y]==-1 || find_miu(match[y]))
{
match[y]=x;
return true;
}
}
else need[y]=min(need[y],gap);
}
return false;
}
int KM()
{
memset(match,-1,sizeof(match));
memset(exb,0,sizeof(exb));
for(int i=1;i<=n;i++)
{
exg[i]=map[i][1];
for(int j=2;j<=n;j++)
exg[i]=max(exg[i],map[i][j]);
}
for(int i=1;i<=n;i++)
{
memset(need,63,sizeof(need));
while(1)
{
memset(visg,false,sizeof(visg));
memset(visb,false,sizeof(visb));
if(find_miu(i)) break;
int d=INF;
for(int j=1;j<=n;j++)if(!visb[j]) d=min(d,need[j]);
for(int j=1;j<=n;j++)
{
if(visg[j]) exg[j]-=d;
if(visb[j]) exb[j]+=d;
else need[j]-=d;
}
}
}
int ans=0;
for(int i=1;i<=n;i++) ans+=map[match[i]][i];
return ans;
}
int main()
{
while(scanf("%d",&n)!=EOF)
{
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
scanf("%d",&map[i][j]);
printf("%d\n",KM());
}
return 0;
}