原理
为了降低时间复杂度,可以在增广匹配集合M时,每次寻找多条增广路径。这样就可以进一步降低时间复杂度,可以证明,算法的时间复杂度可以到达O(sqrt(n)*m)。
基本算法
该算法主要是对匈牙利算法的优化,在寻找增广路径的时候同时寻找多条不相交的增广路径,形成极大增广路径集,然后对极大增广路径集进行增广。在寻找增广路径集的每个阶段,找到的增广路径集都具有相同的长度,且随着算法的进行,增广路径的长度不断的扩大。可以证明,最多增广sqrt(n)次就可以得到最大匹配。
算法流程
(1)从G=(X,Y;E)中取一个初始匹配。
(2)若X中的所有顶点都被M匹配,则表明M为一个完美匹配,返回;否则,以所有未匹配顶点为源点进行一次BFS,标记各个点到源点的距离。
(3)在满足dis[v] = dis[u] + 1的边集
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <vector>
#include <queue>
#include <algorithm>
#define ll long long
using namespace std;
const int inf=0x3ffffff;
const int MAXN = 311;
const int MAXM = 100008;
const double eps = 1e-6;
int n1, n2;
vector<int> g[MAXN];
int mx[MAXN], my[MAXN];
// 表示二分图左右部顶点匹配节点
queue<int> que;
int dx[MAXN], dy[MAXN];
// 表示二分图左右部顶点的距离标号
bool vis[MAXN];
bool find(int u) {
for (int i = 0; i < g[u].size(); i++) {
if (!vis[g[u][i]] && dy[g[u][i]] == dx[u] + 1) {
vis[g[u][i]] = true;
if (!my[g[u][i]] || find(my[g[u][i]])) {
mx[u] = g[u][i];
my[g[u][i]] = u;
return true;
}
}
}
return false;
}
int matching() {
memset(mx, 0, sizeof(mx));
memset(my, 0, sizeof(my));
int ans = 0;
while(1) {
bool flag = false;
while(!que.empty()) {
que.pop();
}
memset(dx, 0, sizeof(dx));
memset(dy, 0, sizeof(dy));
for (int i = 1; i <= n1; i++) {
if (!mx[i]) {
que.push(i);
}
}
while(!que.empty()) {
int u = que.front();
que.pop();
for (int i = 0; i < g[u].size(); i++) {
if (!dy[g[u][i]]) {
dy[g[u][i]] = dx[u] + 1;
if (my[g[u][i]]) {
dx[my[g[u][i]]] = dy[g[u][i]] + 1;
que.push(my[g[u][i]]);
} else {
flag = true;
}
}
}
}
if (!flag) {
break;
}
memset(vis, false, sizeof(vis));
for (int i = 1; i <= n1; i++) {
if (!mx[i] && find(i)) {
ans++;
}
}
}
return ans;
}
int main() {
#ifndef ONLINE_JUDGE
freopen("1.txt", "r", stdin);
#endif
int i, j, k;
int T;
cin >> T;
while(T--) {
cin >> n1 >> n2;
for (i = 1; i <= n1; i++) {
g[i].clear();
scanf("%d", &k);
while(k--) {
scanf("%d", &j);
g[i].push_back(j);
}
}
if (n1 > n2 || matching() != n1) {
cout << "NO" << endl;
} else {
cout << "YES" << endl;
}
}
return 0;
}