匈牙利算法解无边权二分图最大匹配 入门教程

基本理论推荐阅读


先贴个板子

#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;
}

导弹防御塔
人菜,不会


蚂蚁
人菜,不会

posted @ 2020-04-30 21:23  xwmwr  阅读(207)  评论(0编辑  收藏  举报