二分图最大匹配|UOJ#78|匈牙利算法|边表|Elena

时间:2023-12-01 15:03:38

#78. 二分图最大匹配

从前一个和谐的班级,有 nlnl 个是男生,有 nrnr 个是女生。编号分别为 1,…,nl1,…,nl 和 1,…,nr1,…,nr。

有若干个这样的条件:第 vv 个男生和第 uu 个女生愿意结为配偶。

请问这个班级里最多产生多少对配偶?

输入格式

第一行三个正整数,nl,nr,mnl,nr,m。

接下来 mm 行,每行两个整数 v,uv,u 表示第 vv 个男生和第 uu 个女生愿意结为配偶。保证 1≤v≤nl1≤v≤nl,1≤u≤nr1≤u≤nr,保证同一个条件不会出现两次。

输出格式

第一行一个整数,表示最多产生多少对配偶。

接下来一行 nlnl 个整数,描述一组最优方案。第 vv 个整数表示 vv 号男生的配偶的编号。如果 vv 号男生没配偶请输出 00。

样例一

input

2 2 3
1 1
1 2
2 1

output

2
2 1

explanation

11 号男生跟 22 号女生幸福地生活在了一起~

22 号男生跟 11 号女生幸福地生活在了一起~

样例二

input

2 2 2
1 1
2 1

output

1
1 0

explanation

班上一个女神一个女汉子,两个男生都去追女神。一种最优方案是:

11 号男生跟 11 号女生幸福地生活在了一起~

22 号男生孤独终生。= =||

限制与约定

1≤nl,nr≤5001≤nl,nr≤500,1≤m≤2500001≤m≤250000。

时间限制:1s1s

空间限制:256MB


这个题目实际上就是二分图匹配的模板题。
--------
这里请允许我狠狠地吐槽《啊哈!算法》《信息学奥赛一本通》一类的野鸡书。语言很通俗,外表看起来很花俏没错,但错漏百出。《啊哈!算法》里面有相当一部分内容是有严重错误的,dijkstra、最小生成树、邻接表、匈牙利算法部分都有问题,甚至是代码问题,模板让你交上去wa成狗。
但我不得不说,上面两本书是适合新手的。
--------
这里用的是匈牙利算法,题目很老实没什么坑点。
注意:边表数组要开到500000以上,因为是无向图,要建两条边。
注意:match[i]存储i的配偶,1-n1是男方;n1+1-n1+n2是女方,结果要输出男方的配偶,也就是结果要输出match[i](1-n1)。
注意:对每一个点进行增广路时要判断那个点的配偶是否已经确认了,也就是判断match[i]是否为0。
注意:对每一个点进行增广路前要把用来标记的数组也就是book数组清0,再book[i]=1;意味着i点访问过了。
注意:输出的时候如果match[i]!=0match[i]要-n1,否则输出0,原因很简单,自己想。

讲讲dfs的代码。

bool dfs(int u)//寻找u节点的配偶
{
  for (int i=head[u]; i; i=edge[i].next) //枚举以u为出点的每一条边
  if (book[edge[i].to]==0) {//如果某个节点还没有访问过
    book[edge[i].to]=1;//标记已访问过
    if (match[edge[i].to]==0||dfs(match[edge[i].to])) {//match[edge[i].to]==0意味着edge[i].to点还没有配偶;如果edge[i].to点有配偶了,就dfs询问edge[i].to点的配偶能不能“让位”去找别的配偶。这里有点小三上位的意思。如果match[edge[i].to]点找到了新的配偶,就会和edge[i].to点离婚,这样子u点就可以和edge[i].to点在一起啦!
      match[edge[i].to]=u;//标记互为配偶
      match[u]=edge[i].to;
      return 1;//找到配偶
    }
  }
  return 0;//没有找到
}

我已经详细地解释了匈牙利代码的主要部分。

 #include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
using namespace std;
int read()
{
int x=,f=; char c=getchar();
while (c<''||c>'') {if (c=='-') f=-; c=getchar();}
while (c>=''&&c<='') {x=x*+c-'';c=getchar();}
return x*f;
}
long long sum;
int num_edge,head[],match[],u,v,n1,n2,m;
bool book[];
struct Edge
{
int next;
int to;
}edge[];
void add_edge(int from,int to)
{
edge[++num_edge].next=head[from];
edge[num_edge].to=to;
head[from]=num_edge;
}
bool dfs(int u)
{
for (int i=head[u]; i; i=edge[i].next)
if (book[edge[i].to]==) {
book[edge[i].to]=;
if (match[edge[i].to]==||dfs(match[edge[i].to])) {
match[edge[i].to]=u;
match[u]=edge[i].to;
return ;
}
}
return ;
}
int main()
{
n1=read(); n2=read(); m=read();
for (int i=; i<=m; i++) {
u=read(); v=read()+n1;
add_edge(u,v);
add_edge(v,u);
}
for (int i=; i<=n1+n2; i++) {
for (int j=; j<=n1+n2; j++) book[j]=;
book[i]=;
if (match[i]==&&dfs(i)) sum++;
}
printf("%lld\n",sum);
for (int i=; i<=n1; i++)
if (match[i]!=) printf("%d ",match[i]-n1);
else printf("0 ");
return ;
}

二分图最大匹配

有问题可以直接在评论里面提问,有需要转载的请得到我的允许,否则按侵权处理。


Elena loves NiroBC forever!