1. 程式人生 > >網路流三·二分圖多重匹配

網路流三·二分圖多重匹配

描述

學校的秋季運動會即將開始,為了決定參賽人員,需要統計分配比賽選手。

已知班級一共有N名學生,編號依次為1..N。

運動會一共有M項不同的比賽,編號為1..M。第i項比賽每個班需要派出m[i]名選手參加。

根據統計,編號為i的學生表示最多同時參加a[i]項比賽,並且給出他所擅長的b[i]項比賽的編號。

希望將每個學生都安排到他所擅長的比賽專案,以增加奪冠的可能性。同時又要考慮滿足每項比賽對人數的要求,當然給一個學生安排的比賽專案也不能超過他願意參加的比賽專案數量。

根據統計的結果,想知道能否有一個合適的安排,同時滿足這些條件。

輸入

第1行:1個整數T,表示一共有T(2≤T≤5)組資料,每組資料按如下格式給出:

第1行:2個正整數N,M。1≤N≤100,1≤M≤100。

第2行:M個整數,第i個數表示第i個專案需要的選手數量m[i]。1≤m[i]≤N。

第3..N+2行:若干整數,第i+2行表示編號為i的學生的資訊。先是a[i],b[i],接下來b[i]個整數,表示其所擅長的b[i]個專案。1≤a[i]≤M

輸出

第1..T行:第i行表示第i組資料能否滿足要求,若能夠輸出"Yes",否則輸出"No"。

樣例輸入
2
4 3
1 2 2
1 2 1 2
2 2 1 3
1 1 2
1 2 2 3
4 3
2 2 2
1 2 1 2
2 2 1 3
1 1 2
1 2 2 3
樣例輸出
Yes
No

提示:二分圖多重匹配


我們仍然將學生看作A部,比賽專案看作B部:

接下來,根據每個學生的意願我們將對應的A[i]和B[j]連起來。比如編號為i的學生擅長編號為j的專案,那麼就連線A[i]-B[j]:

我們需要對之前的二分圖進行進一步擴充套件。首先虛擬一個源點s和匯點t:

接下來我們根據a[i],在s和A[i]之間連線一條容量為a[i]的邊。

同時將原來A[i]-B[j]直接的邊都改造為從A[i]到B[j]的容量為1的邊。

最後我們還要連線所有的B[j]-t。由於比賽專案B[j]最多隻需要m[j]名選手參加,所以我們不妨限制B[j]-t的容量為m[j]。

這就是我們最後的網路流圖。

在完成最大流後,這個網路流圖的流量情況直接對應了一個分配方案:

s-A[i]:這一類邊的流量表示了A[i]參加的專案數量。

A[i]-B[j]:這一類邊的流量表示了A[i]是否參加專案B[j],若參加則流量為1,否則流量為0。

B[j]-t:這一類邊的流量表示了參加比賽專案B[j]的選手數量。

由於流網路會自動調整去滿足最大流量,所以它會自動調整每個A[i]-B[j]的流量來使得B[j]-t儘可能大。

若每個專案都能夠滿足人數的話,網路流會自己調整為所有B[j]-t都滿流的情況。

我們只需要最後判斷一下每一條B[j]-t的邊是否滿流,就可以知道能否滿足需求。同時還可以根據A[i]-B[j]的情況,計算出每個選手所參加的比賽專案。

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#define MAX 205
#define MAXCF 101
#define min(a,b) (a)>(b)?(b):(a)
using namespace std;

int cf[MAX][MAX];//儲存圖
int queue[MAX];//搜尋隊列
int path[MAX];//儲存路徑
int capacity[MAX];//流量陣列,儲存經過該點的最小流量
bool visited[MAX];//記錄訪問陣列

int findAugmentPath(int T)
{
	int i = 0, tail = 0;
	memset(visited, 0, sizeof(visited));

	queue[tail] = 1;//將源點加入佇列
	capacity[1] = MAXCF;
	visited[1] = true;
	while (i <= tail)
	{
		int u = queue[i];
		if (u == T)
			return capacity[T];//找到一條增廣路徑,返回該路徑最小流量
		for (int v = 2; v <= T; v++)
		{
			if (!visited[v] && cf[u][v] > 0)
			{
				path[v] = u;
				capacity[v] = min(cf[u][v], capacity[u]);//記錄路徑上的最小殘餘流量
				visited[v] = true;
				tail++;
				queue[tail] = v;
			}
		}
		i++;
	}
	return 0;
}

void modifyGraph(int T)
{
	int flow = capacity[T];
	int now = T;
	while (now != 1)
	{
		int fa = path[now];
		cf[fa][now] -= flow;
		cf[now][fa] += flow;
		now = fa;
	}
}

int main()
{
	int T;
	int N, M;
	int i,j;
	int temp;
	int m[MAX];
	int a[MAX], b[MAX];
	cin >> T;

	while (T--)
	{
		cin >> N >> M;
		int S = 1;
		int E = S + N + M + 1;
		int result = 0;
		memset(cf, 0, sizeof(cf));
		for (i = 0; i < M; ++i)
		{
			cin >> m[i];
			cf[S + N + i + 1][E] = m[i];
			result += m[i];
		}

		for (i = 0; i < N; ++i)
		{
			cin >> a[i] >> b[i];
			cf[S][S + i + 1] = a[i];
			for (j = 0; j < b[i]; ++j)
			{
				cin >> temp;
				cf[S + i + 1][S + N + temp] = 1;
			}
		}
		int maxFlow = 0;
		int delta = 0;
		while (delta = findAugmentPath(E))
		{
			maxFlow += delta;
			modifyGraph(E);
		}
		if (maxFlow == result)
			cout << "Yes" << endl;
		else
			cout << "No" << endl;

	}
	return 0;
}