增广路的性质:
(1)整条路径上没有重复的点。起点在二分图的左半边,终点在右半边,并且左右交替出现。
(2)起点和终点都是未配对的,除起点和终点外其它所有点都是已经配好对的。
(4)有奇数条边。
(5)路径上的所有第奇数条边都不在原匹配中,所有第偶数条边都出现在原匹配中。
(6)把增广路径上的所有第奇数条边加入到原匹配中去,并把增广路径中的所有第偶数条边从原
匹配中删除(这个操作称为增广路径的取反),则新的匹配数就比原匹配数增加了1个。
匈牙利算法 O(VE)
#include <cstdio>
#include <cstring>
using namespace std;
const int N = 310;
int g[N][N];
int linker[N];
int L, R;
bool vis[N];
bool dfs(int l)
{
for(int r = 1; r <= R; ++r)
if(g[l][r] && !vis[r])
{
vis[r] = 1;
if(linker[r]==-1 || dfs(linker[r]))
{
linker[r] = l;
return 1;
}
}
return 0;
}
int hungary()
{
int ret = 0;
memset(linker, -1, sizeof(linker));
for(int l = 1; l <= L; ++l)
{
memset(vis, 0, sizeof(vis));
if(dfs(l))
++ret;
}
return ret;
}
int main()
{
int t;
scanf("%d", &t);
while(t--)
{
scanf("%d %d", &L, &R);
memset(g, 0, sizeof(g));
for(int l = 1; l <= L; ++l)
{
int n;
scanf("%d", &n);
for(int i = 1; i <= n; ++i)
{
int r;
scanf("%d", &r);
g[l][r] = 1;
}
}
if(hungary()==L) printf("YES\n");
else printf("NO\n");
}
return 0;
}
vector优化:处理点数较多的情况(n>1000)
#include <cstdio>
#include <cstring>
#include <vector>
using namespace std;
const int N = 310;
vector <int> g[N];
int linker[N];
int L, R;
bool vis[N];
bool dfs(int l)
{
for(int i = 0; i < g[l].size(); ++i)
if(!vis[g[l][i]])
{
vis[g[l][i]] = 1;
if(linker[g[l][i]]==-1 || dfs(linker[g[l][i]]))
{
linker[g[l][i]] = l;
return 1;
}
}
return 0;
}
int hungary()
{
int ret = 0;
memset(linker, -1, sizeof(linker));
for(int l = 1; l <= L; ++l)
{
memset(vis, 0, sizeof(vis));
if(dfs(l))
++ret;
}
return ret;
}
int main()
{
int t;
scanf("%d", &t);
while(t--)
{
for(int i = 0; i < N; ++i) g[i].clear();
scanf("%d %d", &L, &R);
memset(g, 0, sizeof(g));
for(int l = 1; l <= L; ++l)
{
int n;
scanf("%d", &n);
for(int i = 1; i <= n; ++i)
{
int r;
scanf("%d", &r);
g[l].push_back(r);
}
}
if(hungary()==L) printf("YES\n");
else printf("NO\n");
}
return 0;
}