二分图匹配练习题

关于二分图匹配的讲解见文章二分图匹配

本文练习两个简单的二分图匹配,分别对应于文章中的dfs和bfs的Hungary算法。

1、HDU1150 

题意:两台机器A和B,每台都有许多工作模式。有多个任务,每个任务可以在A机器的某个模式或者在B机器的某个模式完成。问最少需要重启几次机器。

分析:该问题即最小点覆盖,即选取最少的点,使任意一条边至少有一个端点被选择,任务相当于边。最小点覆盖数=最大匹配数。

代码(dfs):

#include <stdio.h>
#include <string.h>
#define N 102

int G[N][N];
int matching[N], check[N];
int n, m, k;
bool dfs(int u){
    for(int i = 1; i <= m; i++){
        if(G[u][i]!=-1 && !check[i]){
            check[i] = 1;
            if(matching[i] == -1 || dfs(matching[i])){
                matching[i] = u;
                return true;
            }
        }
    }
    return false;
}
int main(){
    while(scanf("%d", &n) && n){
        scanf("%d%d", &m, &k);
        int a, b, c;
        memset(G, -1, sizeof(G));
        for(int i = 0; i < k; i++){
            scanf("%d%d%d", &a, &b, &c);
            if(b && c)
                G[b][c] = a;
        }
        int cnt = 0;
        memset(matching, -1, sizeof(matching));
        for(int i = 1; i <= n; i++){
            memset(check, 0, sizeof(check));
            if(dfs(i))
                cnt++;
        }
        printf("%d\n", cnt);
    }
    return 0;
}

2、POJ2446

题意:给一个n*m的表格,表格中有一些格子挖去,用1*2或者2*1的长方形去覆盖,判断是否恰好完全覆盖。

解析:首先需要根据给定的图重新建图,然后求最大匹配数,如果剩余的格子的是最大匹配数的两倍,那么可以完全覆盖。

代码(bfs):

#include <stdio.h>
#include <string.h>
#include <queue>
#include <vector>
using namespace std;
const int N = 32*32+5;
int tol, k;
int a[35][35];
int pre[N], matching[N], check[N];
vector<int>G[N];
queue<int> Q;
int nt[4][2] = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}};
bool Hungary(){
	int ans = 0;
	memset(matching, -1, sizeof(matching));
	memset(check, -1, sizeof(check));
	for(int i = 1; i <= tol; i++){
		if(matching[i] == -1){
			while(!Q.empty())Q.pop();
			Q.push(i);
			pre[i] = -1;
			bool flag = false;
			while(!Q.empty() && !flag){
				int u = Q.front();
				Q.pop();
				for(int j = 0; j < G[u].size() && !flag; j++){
					int v = G[u][j];
					if(check[v] != i){
						check[v] = i;
						Q.push(matching[v]);
						if(matching[v] != -1){
							pre[matching[v]] = u;
						}else{
							flag = true;
							int d = u, e = v;
							while(d != -1){
								int t = matching[d];
								matching[d] = e;
								matching[e] = d;
								d = pre[d];
								e = t;
							}
						}
					}
				}
			}
			if(matching[i] != -1)ans++;
		}
	}
	if((tol - k) / 2 == ans)return true;
	return false;
}
int main(){
	int x, y, n, m;
	while(~scanf("%d%d%d", &n, &m, &k)){
		memset(a, 0, sizeof(a));
		for(int i = 0; i < k; i++){
			scanf("%d%d", &y, &x);
			a[x][y] = 1;
		}
		tol = n * m;
		if((tol - k) % 2){
			printf("NO\n");
			continue;
		}
		for(int i = 1; i <= tol; i++)
			G[i].clear();
		for(int i = 1; i <= n; i++)
			for(int j = 1; j <= m; j++){
				if(a[i][j] == 1)continue;
				int nw = (i-1)*m + j;
				for(int k = 0; k < 4; k++){
					int nx = i + nt[k][0];
					int ny = j + nt[k][1];
					if(nx <= 0 || nx > n || ny <= 0 || ny > m)continue;
					if(a[nx][ny] == 1)continue;
					int p = (nx-1)*m + ny;
					G[nw].push_back(p);
				}
			}
		bool flag = Hungary();
		if(flag)printf("YES\n");
		else printf("NO\n");
	}
	return 0;
}

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值