最大匹配 人員分配【三種方法】

最大匹配 人員分配

題目鏈接:ssl 1338

題目

設有MM個工人x1x_1, x2x_2, , xmx_m,和NN項工作y1y_1, y2y_2, , yny_n,規定每個工人至多做一項工作,而每項工作至多分配一名工人去做。由於種種原因,每個工人只能勝任其中的一項或幾項工作。問應怎樣分配才能使盡可能多的工人分配到他勝任的工作。這個問題稱爲人員分配問題。

輸入

第一行兩個整數mmnn分別爲工人數和工作數。
接下來一個整數ss,爲二分圖的邊數。
接下來ss行,每行兩個數aia_i,bib_i表示第aia_i個工人能勝任第bib_i份工作

輸出

一個整數,表示最多能讓多少個工人派到自己的勝任的工作上。

樣例輸入

3 3
4
1 2
2 1
3 3
1 3

樣例輸出

3

數據範圍

1<=m,n<=1001<=m,n<=100
1<=s<=100001<=s<=10000

思路

模板題。

思路1

用鄰接矩陣++匈牙利算法

代碼1

#include<cstdio>
#include<cstring>

using namespace std;

int n, m, s, x, y, k, temp, l[105], ans;
bool in[101], a[101][101];

int find(int now) {//匈牙利算法
	for (int i = 1; i <= n; i++)
		if (!in[i] && a[now][i]) {
			in[i] = 1;
			temp = l[i];
			l[i] = now;
			if (!temp || find(temp)) return 1;
			l[i] = temp;
		}
		
	return 0;
}

int main() {
	scanf("%d %d %d", &n, &m, &s);//讀入
	
	for (int i = 1; i <= s; i++) {
		scanf("%d %d", &x, &y);//讀入
		a[x][y] = 1;//建圖
	}
	
	for (int i = 1; i <= n; i++) {//匈牙利算法
		memset(in, 0, sizeof(in));
		ans += find(i);
	}
	
	printf("%d", ans);//輸出
	
	return 0;
}

思路2

用鄰接表++匈牙利算法。

代碼2

#include<cstdio>
#include<cstring>

using namespace std;

struct note {
	int to, next;
}e[10001];
int n, m, s, x, y, le[101], k, temp, l[105], ans;
bool in[101];

void add(int x, int y) {//連邊
	e[++k] = (note){y, le[x]}; le[x] = k;
}

int find(int now) {//匈牙利
	for (int i = le[now]; i; i = e[i].next)
		if (!in[e[i].to]) {
			in[e[i].to] = 1;
			temp = l[e[i].to];
			l[e[i].to] = now;
			if (!temp || find(temp)) return 1;
			l[e[i].to] = temp;
		}
		
	return 0;
}

int main() {
	scanf("%d %d %d", &n, &m, &s);//輸入
	
	for (int i = 1; i <= s; i++) {
		scanf("%d %d", &x, &y);//輸入
		add(x, y);//連邊
	}
	
	for (int i = 1; i <= n; i++) {
		memset(in, 0, sizeof(in));//初始化
		ans += find(i);//匈牙利
	}
	
	printf("%d", ans);//輸出
	
	return 0;
}

思路3

鄰接表+dinic+dinic算法
(其實這種做法之前寫過)
要詳細一點的可以看下面的鏈接:
——>戳這裏<——

代碼3

#include<cstring>
#include<cstdio>
#include<queue>

using namespace std;

struct note {
	int to, next, op, now;
}e[3000001];
int n, m, o, x, y, le[10001], k, s, t, ans, dis[10001];
queue<int>q;

bool bfs() {
	while (!q.empty()) q.pop();
	memset(dis, 0x7f, sizeof(dis));
	dis[s] = 0;
	
	q.push(s);
	while (!q.empty()) {
		int now = q.front();
		q.pop();
		
		for (int i = le[now]; i; i = e[i].next)
			if (dis[e[i].to] > dis[now] + 1 && e[i].now) {
				dis[e[i].to] = dis[now] + 1;
				if (e[i].to == t) return 1;
				q.push(e[i].to);
			}
	}
	
	return 0;
}

int dfs(int now, int an) {
	if (now == t) return an;
	
	int go = 0;
	for (int i = le[now]; i; i = e[i].next)
		if (dis[e[i].to] == dis[now] + 1 && e[i].now) {
			int line_go = dfs(e[i].to, min(e[i].now, an - go));
			if (!line_go) dis[e[i].to] = -1;
			e[i].now -= line_go;
			e[e[i].op].now += line_go;
			
			go += line_go;
			if (go == an) break;
		}
	
	return go;
}

int main() {
	scanf("%d %d %d", &n, &m, &o);//讀入
	
	s = n + m + 1;//設置源點匯點
	t = n + m + 2;
	
	for (int i = 1; i <= o; i++) {
		scanf("%d %d", &x, &y);//讀入
		y += n;
		e[++k] = (note){y, le[x], k + 1, 1}; le[x] = k;//建圖
		e[++k] = (note){x, le[y], k - 1, 0}; le[y] = k;
	}
	
	for (int i = 1; i <= n; i++) {
		e[++k] = (note){i, le[s], k + 1, 1}; le[s] = k;//連源點
		e[++k] = (note){s, le[i], k - 1, 0}; le[i] = k;
	}
	for (int i = n + 1; i <= n + m; i++) {
		e[++k] = (note){t, le[i], k + 1, 1}; le[i] = k;//連匯點
		e[++k] = (note){i, le[t], k - 1, 0}; le[t] = k;
	}
	
	while (bfs())//dinic算法
		ans += dfs(s, 2147483647);
	
	printf("%d", ans);//輸出
	
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章