匈牙利算法解无边权二分图最大匹配 入门教程
目录
先贴个板子
#include<bits/stdc++.h>
using namespace std;
const int maxn = 505;
const int maxe = 50005;
int n,m,e;
int ct,hed[maxn],ver[maxe],nt[maxe];
void ad(int a,int b) {
ver[++ct]=b, nt[ct]=hed[a], hed[a]=ct;
}
int mc[maxn], vis[maxn];
bool dfs(int x) {
for(int i=hed[x],y;i;i=nt[i]) if(!vis[y=ver[i]]) {
vis[y]=1;
if(!mc[y]||dfs(mc[y])) { //找到增广路
mc[y]=x;
return true;
}
}
return false;
}
int main()
{
scanf("%d%d%d", &n,&m,&e);
for(int i=0,x,y;i<e;++i) {
scanf("%d%d", &x,&y), ad(x,y);
}
int ans = 0;
for(int i=1;i<=n;++i) {
memset(vis,0,sizeof vis);
if(dfs(i)) ++ans;
//找到一条增广路并实施增广可以使最大匹配数加一
}
cout<<ans;
return 0;
}
例题推荐
[ZJOI2007]矩阵游戏
发现无论怎么将一个有解的矩阵进行行列交换, 任意时刻其总是拥有 \(n\) 个黑点使得其行标号两两不同且其列标号两两不同。
连一连画一画发现就是个无边权二分图最大匹配的问题, 一侧是行, 另一侧是列, 一条边表示一个黑点。
贴下AC代码
#include<bits/stdc++.h>
using namespace std;
const int maxn = 205;
int n;
int ct,hed[maxn],ver[maxn*maxn],nt[maxn*maxn];
void ad(int a,int b) {
ver[++ct]=b, nt[ct]=hed[a], hed[a]=ct;
}
int vis[maxn], mc[maxn];
bool dfs(int x) {
for(int i=hed[x],y;i;i=nt[i]) if(!vis[y=ver[i]]) {
vis[y]=1;
if(!mc[y]||dfs(mc[y])) {
mc[y]=x;
return true;
}
}
return false;
}
int main()
{
int T; cin>>T; while(T--) {
ct=0;
memset(hed,0,sizeof hed);
memset(mc,0,sizeof mc);
scanf("%d", &n);
for(int i=1,x;i<=n;++i) for(int j=1;j<=n;++j) {
scanf("%d",&x); if(x) ad(i,j);
}
int ans = 0;
for(int i=1;i<=n;++i) {
memset(vis,0,sizeof vis);
if(dfs(i)) ++ans;
}
if(ans==n) puts("Yes");
else puts("No");
}
return 0;
}
棋盘覆盖
将棋盘奇偶分类(按行标列标之和的奇偶性分类每个格子)(也叫黑白染色)。
然后就可以二分图搞了, 注意建图的时候好好思考下细节。
贴下AC代码
#include<bits/stdc++.h>
using namespace std;
const int maxn = 205;
int n, t;
bool vis[maxn][maxn];
int dx[4]={0,0,1,-1};
int dy[4]={1,-1,0,0};
int ct,hed[maxn*maxn],nt[maxn*maxn],ver[maxn*maxn];
void ad(int a,int b) {
ver[++ct]=b,nt[ct]=hed[a],hed[a]=ct;
}
int v[maxn*maxn], mc[maxn*maxn];
bool dfs(int x) {
for(int i=hed[x],y;i;i=nt[i]) if(!v[y=ver[i]]) {
v[y]=1;
if(!mc[y]||dfs(mc[y])) {
mc[y]=x;
return true;
}
}
return false;
}
int main()
{
scanf("%d%d", &n, &t);
for(int i=0,x,y;i<t;++i) {
scanf("%d%d", &x,&y); vis[x][y] = true;
}
for(int i=1; i<=n; ++i)
for(int j=1; j<=n; ++j) if(!vis[i][j] && (i+j)&1) {
for(int k=0;k<4;++k) {
int x=i+dx[k], y=j+dy[k];
if(x<1||x>n||y<1||y>n||vis[x][y]) continue;
ad((i-1)*n+j, (x-1)*n+y);
}
}
int ans=0;
for(int i=1;i<=n;++i)
for(int j=1;j<=n;++j) if(!vis[i][j] && (i+j)&1) {
memset(v,0,sizeof v);
if(dfs((i-1)*n+j)) ++ans;
}
cout<<ans;
return 0;
}
車的放置
所有车的行标两两不同,列标两两不同。
剩下的就是和 [ZJOI2007矩阵游戏] 一样, 用边表示一个格子, 然后跑无边权二分图最大匹配就行了。
AC代码
#include<bits/stdc++.h>
using namespace std;
const int maxn = 205;
int n,m,t;
bool vis[maxn][maxn];
int ct,hed[maxn],ver[maxn*maxn],nt[maxn*maxn];
void ad(int a,int b) {
ver[++ct]=b, nt[ct]=hed[a], hed[a]=ct;
}
int mc[maxn], v[maxn];
bool dfs(int x) {
for(int i=hed[x],y;i;i=nt[i]) if(!v[y=ver[i]]) {
v[y]=1;
if(!mc[y]||dfs(mc[y])) {
mc[y]=x;
return true;
}
}
return false;
}
int main()
{
scanf("%d%d%d", &n,&m,&t);
for(int i=0,x,y;i<t;++i) {
scanf("%d%d", &x,&y); vis[x][y]=1;
}
for(int i=1;i<=n;++i)
for(int j=1;j<=m;++j)
if(!vis[i][j]) ad(i,j);
int ans=0;
for(int i=1;i<=n;++i) {
memset(v,0,sizeof v);
if(dfs(i)) ++ans;
}
cout<<ans;
return 0;
}
导弹防御塔
人菜,不会
蚂蚁
人菜,不会