1、什么是二分图
可以把顶点分成X,Y两个集合,且每个集合里没有相邻的边(相连)。
2、二分图有哪些问题
2.1 二分图的判定
关于二分图的判定,一般使用染色判断法,即把边两端的定点颜色染成不同颜色观察是否冲突。
模板:
bool judge(int u){
for (int i=head[u];~i;i=e[i].nxt){
int v=e[i].to;
if (!col[v]){
col[v]=!col[u];
if (!judge(v)) return 0;
}
else if (col[v]==col[u])
return 0;
}
return 1;
}
if (judge(1)) return true;
2.2 二分图匹配问题
2.2.1 二分图最大匹配
采用匈牙利算法,注意顶点数/2,O(VE),有bfs和dfs两个版本,对于稀疏图bfs快,稠密图差不多,dfs优点在于好写。
bool dfs(int u){
for (int i=head[u];~i;i=e[i].nxt){
int v=e[i].to;
if (!vis[v]){
vis[v]=1;
if (link[v]==-1 || dfs(link[v])){
link[v]=u;
return 1;
}
}
}
return 0;
}
int match(){
int ans=0;
memset(link,-1,sizeof(link));
for (int i=1;i<=n;i++){
memset(vis,0,sizeof(vis));
if (dfs(i)) ans++;
}
return ans;
}
2.2.2 二分图完全匹配
完全匹配是一个验证问题,只要把2.2.1的ans和顶点数/2相比即可。
2.2.3 二分图带权匹配
KM算法,O(n^3)。
// 最小费用最大流 KM算法 带权二分图匹配
// KM算法是求最大权值匹配,求最小权值匹配,只需要将所有边权取负。
// 边权之积最大 取对数
// S->{X} 权值为0,容量为1。
// {Y}->T 权值为0,容量为1。
// 原有的边 权值不变,容量为1。
// 邻接矩阵 不适合稀疏图
// 如果可以非完全匹配,引入顶点A,{X}->A 容量为1,权值为0.
// A->T 权值为0,容量不小于|X|
const int ME=1010;
const int MV=100;
struct edge{
int to,nxt,flow;
db cost;
}e[ME];
int head[MV],cnt;
queue<int> q;
int cur[MV],pre[MV];
bool used[MV],sign[MV];
int flow,n,m;
db cost,dis[MV];
double p[16][1024];
bool spfa(int s,int t){
memset(used,0,sizeof(used));
memset(sign,0,sizeof(sign));
memset(dis,0,sizeof(dis));
used[s]=sign[s]=1;
while (!q.empty()) q.pop();
q.push(s);
while (!q.empty()){
int u=q.front();
q.pop();
used[u]=0;
for (int i=head[u];~i;i=e[i].nxt){
if (e[i].flow<1) continue;
int v=e[i].to;
db c=e[i].cost;
if (!sign[v] || dis[v]>dis[u]+c){
dis[v]=dis[u]+c;
sign[v]=1;
pre[v]=u;
cur[v]=i;
if (used[v]) continue;
used[v]=1;
q.push(v);
}
}
}
return sign[t];
}
void init(){
cnt=0;
memset(head,-1,sizeof(head));
}
void add(int u,int v,int flow,db cost){
e[cnt].to=v, e[cnt].flow=flow, e[cnt].cost=cost;
e[cnt].nxt=head[u], head[u]=cnt++;
e[cnt].to=u, e[cnt].flow=0, e[cnt].cost=-cost;
e[cnt].nxt=head[v], head[v]=cnt++;
}
void solve(int s,int t){
flow=cost=0;
while (spfa(s,t)){
int tmp=t;
int now=inf;
while (tmp!=s){
now=min(now,e[cur[tmp]].flow);
tmp=pre[tmp];
}
flow+=now;
tmp=t;
while (tmp!=s){
int id=cur[tmp];
cost+=now*e[id].cost;
e[id].flow-=now;
e[id^1].flow+=now;
tmp=pre[tmp];
}
}
}
db getcost(){
return cost;
}
3、可以转化成二分图的问题
3.1 最小路径覆盖
顶点数-最大匹配。
3.2 最小顶点覆盖
最大匹配
3.3 最小边覆盖
最大匹配
3.4 最大独立集
顶点数-最大匹配。