2025年2月20日¶
二分图¶
二分图(Bipartite graph) 是一种特殊的图,可以将图里的所有点分为两个集合 \(V1\) , \(V2\) ,这张图上的所有边都是一个点在 \(V1\) 中 ,一个点在 \(V2\) 中。
性质¶
如果两个集合种的点分别染成黑色和白色,二分图种的每一条边一定都是连接一个黑色点和一个白色点。即二分图中,每个点的相邻点一定都是颜色相反的。
二分图不存在长度为奇数的环。
可以通过 DFS 或 BFS 来判定二分图。
应用¶
二分图性质
这个就是去判定每个连通块是否是二分图。
C++
void solve()
{
int n;
cin>>n;
vi x(n+1),y(n+1),r(n+1);
for(int i=1;i<=n;i++)
cin>>x[i]>>y[i]>>r[i];
vector<vi>adj(n+1,vi());
auto calc=[&](int x){
return 1ll*x*x;
};
for(int i=1;i<=n;i++)
for(int j=i+1;j<=n;j++){
if(calc(x[i]-x[j])+calc(y[i]-y[j])==calc(r[i]+r[j])){
adj[i].push_back(j);
adj[j].push_back(i);
}
}
vi s(n+1,-1);
for(int i=1;i<=n;i++){
if(s[i]!=-1) continue;
bool ok=true;
int x=0,y=0;
auto dfs=[&](auto &&self,int u)->void{
if(s[u]) x++;
else y++;
// cerr<<u<<'\n';
for(auto v:adj[u]){
if(s[v]!=-1){
if(s[v]==s[u]) ok=false;
continue;
}
s[v]=s[u]^1;
self(self,v);
}
};
s[i]=0;
dfs(dfs,i);
if(ok&&x!=y){
cout<<"YES\n";
return;
}
}
cout<<"NO\n";
}
二分图最大匹配¶
从二分图里选出若干条边,每条边都不相交,且边的数量最多,就是二分图的最大匹配。
C++
void solve()
{
int n,m,e;
cin>>n>>m>>e;
vector<vi>adj(n+1,vi());
for(int i=1;i<=e;i++){
int u,v;
cin>>u>>v;
adj[u].push_back(v);
}
vector<int>match(m+1);
int ans=0;
for(int i=1;i<=n;i++){
vector<bool>sel(m+1);//记有没有被选过
auto dfs=[&](auto &&self,int u)->bool{
for(auto v:adj[u]){
if(!sel[v]){
sel[v]=true;
if(!match[v]||self(self,match[v])){
match[v]=u;
return true;
}
}
}
return false;
};
if(dfs(dfs,i))
ans++;
}
cout<<ans<<'\n';
}
选取左边一个点,看能不能找到一个右侧的点和它匹配。如果已经匹配,则看能不能让它已匹配的点去尝试匹配别的点,如果可以匹配,那么直接答案+1。
这个就是叫 匈牙利算法
,每次去找增广路径。