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 最大独立集
顶点数-最大匹配。