HDU 2426 Interesting Housing Problem(二分图最优匹配)
http://acm.hdu.edu.cn/showproblem.php?pid=2426
题意:
有N个学生和M个房间,每个学生可能会给多个房间打分.如果分数>=0,表示他可以选择这个房间.(如果分数<0,那么他不喜欢这个房间且他不能选这个房间). 且那些他没打过分的房间他也不能选.
现在问你是否存在一个匹配使得每个学生分得一个单独的房间且每个房间最多1个学生.且这些房间都是学生打了分的且分数>0的房间? 如果存在输出分数和的最大值,如果不存在输出-1.
分析:
首先明显当输出N>M的时候直接输出-1.
下面的情况就是N<=M时了.首先注意学生一定只能选那些他打了分的且分数>=0的房间.
由于我们要求最优匹配用的KM算法,那么我们就应该用点集大小不对称的KM算法模板且由于最优匹配要求所有边都必须要有权值才行.
那么我们对于那些原本不存在的边就赋值为负无穷.那么当我们求出最优匹配的时候,如果存在某条匹配边的权值是负无穷,那么代表本问题无解. 否则的话总权值就是最优匹配的权值解.
AC代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
#define INF 1e9
using namespace std;
const int maxn=500+10;
struct Max_Match
{
int n,m,W[maxn][maxn];
int Lx[maxn],Ly[maxn];
bool S[maxn],T[maxn];
int left[maxn];
bool match(int i)
{
S[i]=true;
for(int j=1;j<=m;j++)if(Lx[i]+Ly[j]==W[i][j] && !T[j])
{
T[j]=true;
if(left[j]==-1 || match(left[j]))
{
left[j]=i;
return true;
}
}
return false;
}
void update()
{
int a=1<<30;
for(int i=1;i<=n;i++)if(S[i])
for(int j=1;j<=m;j++)if(!T[j])
a = min(a, Lx[i]+Ly[j]-W[i][j]);
for(int i=1;i<=n;i++)if(S[i]) Lx[i]-=a;
for(int j=1;j<=m;j++)if(T[j]) Ly[j]+=a;
}
int solve(int n,int m)
{
this->n=n;
this->m=m;
memset(left,-1,sizeof(left));
memset(Lx,0,sizeof(Lx));
memset(Ly,0,sizeof(Ly));
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
Lx[i]=max(Lx[i],W[i][j]);
for(int i=1;i<=n;i++)
{
while(true)
{
memset(S,0,sizeof(S));
memset(T,0,sizeof(T));
if(match(i)) break;
else update();
}
}
int ans=0;
for(int i=1;i<=m;i++)if(left[i]!=-1)
{
if(W[left[i]][i] != -INF)
ans += W[left[i]][i];
else return -1;
}
return ans;
}
}KM;
int main()
{
int n,m,e,kase=0;
while(scanf("%d%d%d",&n,&m,&e)==3)
{
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
KM.W[i][j]=-INF;
for(int i=0;i<e;i++)
{
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
++u,++v;
if(w>=0) KM.W[u][v]=w;
}
printf("Case %d: %d\n",++kase,KM.solve(n,m));
}
return 0;
}