二分图的匹配

匈牙利!

匈牙利算法

  • 二分图的最大匹配,已知左集合和右集合
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
/*
* @Author: Marhoosh
* @Date: 2021-07-22 10:27:29
* @Last Modified by: Marhoosh
* @Last Modified time: 2021-11-03 21:20:12
*/
#include<bits/stdc++.h>
using namespace std;
#define Debug(x) cout<<#x<<':'<<x<<endl
#define INF 0x7fffffff
typedef long long ll;
typedef pair<ll,ll> P;
const ll maxn=1e3+11;
const ll maxm=1e5+11;
struct Edge{
ll to,nx;
}e[maxm];
ll head[maxn],vis[maxn],match[maxn];
ll tmp=2,n1,n2,m;
void add(ll u,ll v){
e[tmp]={v};e[tmp].nx=head[u];head[u]=tmp++;
}
ll dfs(ll u){
for(ll i=head[u];i;i=e[i].nx){
ll v=e[i].to;
if(!vis[v]){
vis[v]=1;
if(!match[v] || dfs(match[v])){
cout<<v<<" "<<u<<endl;
match[v]=u;return 1;
}
}
}
return 0;
}
void solve(){
ll ans=0;
for(ll i=1;i<=n1;i++){
memset(vis,0,sizeof(vis));
if(dfs(i)) ans++;
}
cout<<ans<<endl;
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
cin>>n1>>n2>>m;
for(ll i=1;i<=m;i++){
ll u,v;cin>>u>>v;
add(u,v+n1);add(v+n1,u);
}
solve();
return 0;
}

  • 图的最大匹配,左集合与右集合未知
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
/*
* @Author: Marhoosh
* @Date: 2021-10-11 11:00:11
* @Last Modified by: Marhoosh
* @Last Modified time: 2021-11-03 21:14:49
*/
#include<bits/stdc++.h>
using namespace std;
#define Debug(x) cout<<#x<<':'<<x<<endl
#define INF 0x7fffffff
typedef long long ll;
typedef pair<ll,ll> P;
const ll maxn=1e3+11;
const ll maxm=2e5+11;
struct Edge{
ll to,nx;
}e[maxm];
ll vis[maxn],match[maxn],head[maxn];
ll n,tmp;
void add(ll u,ll v){
e[tmp]={v};e[tmp].nx=head[u];head[u]=tmp++;
}
ll dfs(ll u){
for(ll i=head[u];i;i=e[i].nx){
ll v=e[i].to;
if(!vis[v]){
vis[v]=1;
if(match[v]==-1 || dfs(match[v])){
match[u]=v;match[v]=u;//匹配当然是一对
return 1;
}
}
}
return 0;
}
void solve(){
for(ll i=0;i<n;i++){
memset(vis,0,sizeof(vis));
if(match[i]==-1) dfs(i);//非匹配点才找增广路
}
ll ans=0;
for(ll i=0;i<n;i++){
if(match[i]!=-1){
ans++;
}
}
cout<<n-ans/2<<endl;
}
int main(){
while(~scanf("%lld",&n)){
memset(head,0,sizeof(head));
memset(match,-1,sizeof(match));tmp=1;
for(ll i=0;i<n;i++){
ll u,m;scanf("%lld: (%lld)",&u,&m);
for(ll j=1;j<=m;j++){
ll v;scanf("%lld",&v);
add(u,v);
}
}
solve();
}
return 0;
}

棋盘游戏 HDU - 1281

题意:在一个n*m(n和m < 100)的方格里,有k个方格可以放置中国象棋里的车。在尽量多放车的情况下,这k中有多少个方格删去后会导致能放的车数量减少。

思路:二分匹配模板题
因为一个方格对应了一个横坐标和一个纵坐标,所以可以将方格看成边,边的两端是其对应的横坐标与纵坐标。所以本题就转化为尽可能多的将横坐标与纵坐标匹配,为二分匹配问题。本题数据比较水,可以枚举删除的点。

Going Home HDU - 1533

题意:在一个r * c(r和c都 <100 )的地图上,有同样数量的房子和人,现要求每个人到一个房子中去,求最短路径长度。

思路:一个人一个房子,一个萝卜一个坑,又有权值,是二分最大带权匹配无疑了。但题目求的是最短路,我们只需要将其转化一下就行。此题最长路不超过200,所以我们可以将房子和人之间的路径长度l转换一下,改成200-l。这样子求最大带权匹配就ok了。