https://blog.csdn.net/gddswlz/article/details/9086207(O(sqrt(n)*m)复杂度证明)
https://blog.csdn.net/discreeter/article/details/51649155(板子来源)
https://blog.csdn.net/qq_35776579/article/details/54945092
看不懂复杂度证明,会用就行了,匹配题建图是核心
如果有谁懂复杂度证明,还望不吝赐教
每次bfs寻找多条长为d的增广路,第一次搜索时d至少为1,第二次d至少为2,依次往上增,
每一次bfs分层之后,把所有为d的增广路通过dfs完成匹配
一次bfs+循环内对应所有dfs的复杂度是O(m)的,
那么由于玄学地不会bfs超过sqrt(n)次,所以复杂度是O(sqrt(n)*m)的
每次把所有没有被匹配的当做距离0多源bfs,对当前匹配的进行距离分层,
匈牙利算法每次对于一个点搜所有边只搜到一条路径,不管长短,所以是O(n*m);
而HK算法每次处理所有长度为d的增广路,将其用一次O(m)的复杂度匹配完
//hopcroft_karp算法,复杂度O(sqrt(n)*m)
#include
#include
#include
#include
#include
#include
using namespace std;
const int N = 320;
const int INF = 0x3f3f3f3f;
struct edge
{
int to, next;
}e[N*N];
int match[N], head[N];
bool used[N];
int p, n;
int nx, ny, cnt, dis; //nx,ny分别是左点集和右点集的点数
int dx[N], dy[N], cx[N], cy[N]; //dx,dy分别维护左点集和右点集的标号
//cx表示左点集中的点匹配的右点集中的点,cy正好相反
void add(int u,int v)
{
e[cnt].to = v;
e[cnt].next = head[u];
head[u] = cnt++;
}
bool bfs() //寻找增广路径集,每次只寻找当前最短的增广路
{
queue que;
dis = INF;
memset(dx, -1, sizeof dx);
memset(dy, -1, sizeof dy);
for(int i = 1; i <= nx; i++)//将未遍历的节点入队,并初始化次节点距离为0
if(cx[i] == -1)
{
que.push(i);
dx[i] = 0;
}
while(!que.empty())
{
int u = que.front();
que.pop();
if(dx[u] > dis) break;
for(int i = head[u]; i != -1; i = e[i].next)
{
int v = e[i].to;
if(dy[v] == -1)
{
dy[v] = dx[u] + 1;
if(cy[v] == -1) dis = dy[v]; //找到了一条增广路,dis为增广路终点的标号
else dx[cy[v]] = dy[v] + 1,que.push(cy[v]);
}
}
}
return dis != INF;
}
int dfs(int u)
{
for(int i = head[u]; i != -1; i = e[i].next)
{
int v = e[i].to;
if(! used[v] && dy[v] == dx[u] + 1) //如果该点没有被遍历过并且距离为上一节点+1
{
used[v] = true;
if(cy[v] != -1 && dy[v] == dis) continue; //u已被匹配且已到所有存在的增广路终点的标号,再递归寻找也必无增广路,直接跳过
if(cy[v] == -1 || dfs(cy[v]) )
{
cy[v] = u,cx[u] = v;
return 1;
}
}
}
return 0;
}
int hopcroft_karp()
{
int res = 0;
memset(cx, -1, sizeof cx);
memset(cy, -1, sizeof cy);
while(bfs())
{
memset(used, 0, sizeof used);
for(int i = 1; i <= nx; i++)
if(cx[i] == -1)res += dfs(i);
}
return res;
}
int main()
{
int t, a, b;
scanf("%d", &t);
while(t--)
{
cnt = 0;
memset(head, -1, sizeof head);
scanf("%d%d", &p, &n);
for(int i = 1; i <= p; i++)
{
scanf("%d", &a);
for(int j = 0; j < a; j++)
{
scanf("%d", &b);
add(i, b);
}
}
nx = p, ny = n;
if(hopcroft_karp() == p) printf("YES\n");
else printf("NO\n");
}
return 0;
}