**~~
**~~
性质:1.无奇圈
2.对于网格图,以每个格子为点,图为二分图。
染色法判图是否为二分图
int color[MAX_N];
bool dfs(int u,int c)
{
color[u]=c;
reps(u){
if(color[v]){
if(color[v]==c) return false;
continue;
}
if(!dfs(v,-c)) return false;
}
return true;
}
bool Bipartite_Graph_Check(int u)
{
ms(color);
if(dfs(u,1)) return true;
else return false;
}
二分图匹配
最大匹配:在二分图中,最多的没有公共顶点的边的数量。
最小点覆盖:在二分图中,所有边至少有一个顶点在一个点集S中,这样的点集S的最少元素个数。(最小点覆盖=最大匹配)
最大独立点集:在二分图中,选择一些点,使得这些点两两没有边直接相连。(最大独立点集=总点数-最大匹配)
DAG转化成二分图:将一个点拆分成两个点:入点和出点,连一条边(如A→B),就把A的出点和B的入点相连。
DAG的链:一条从A走到B的路径上的所有点。
最小不相交路径覆盖:在DAG中,用最少的不相交的链覆盖整个DAG上的点(转化成二分图,则最小路径覆盖=点数(原点数,非拆点后点数)-二分图的最大匹配)
最小可相交路径覆盖:先用floyd求传递闭包(两点间的可达性),利用求出的新图按最小不相交路径覆盖处理。
模板
二分匹配(Hungary O ( V E ) O(VE) O(VE))
struct Edge{ int to,nxt; };
struct Bipartite_matching{
int nl,nr;//nl左侧点数,nr右侧点数,1 - nl左侧点nl+1 - nr右侧点
Edge e[MAX_E<<1];
int head[MAX_N],tote;
int match[MAX_N];
bool used[MAX_N];
void init(int l,int r)
{
nl=l,nr=r,tote=0;
repi(i,1,nl+nr) head[i]=match[i]=0;
}
void add_edge(int u,int v)
{
e[++tote].to=v,e[tote].nxt=head[u],head[u]=tote;
e[++tote].to=u,e[tote].nxt=head[v],head[v]=tote;
}
bool dfs(int u)
{
used[u]=true;
reps(u){
int w=match[v];
if(!w||!used[w]&&dfs(w)){
match[u]=v,match[v]=u;
return true;
}
}
return false;
}
int cal()//判是否是完美匹配时若一个点没匹配到可直接返回0不用求完
{
int res=0;
repi(i,1,nl)if(!match[i]){
repi(j,1,nl+nr) used[j]=false;
if(dfs(i)) res++;
}
return res;
}
}bm;
二分匹配( Hopcroft_Carp O ( s q r t ( v ) E ) O(sqrt(v)E) O(sqrt(v)E) )
const int inf=0x3f3f3f3f;
struct Edge{ int to,nxt; };
struct Hopcroft_Carp{
int um[MAX_N],vm[MAX_N];
int dx[MAX_N],dy[MAX_N],dis;
bool vis[MAX_N];
Edge e[MAX_E];
int head[MAX_N],tote;
void init(int n){ repi(i,1,n) head[i]=0; tote=0; }//n为左边点数
void add_edge(int u,int v){ e[++tote].to=v,e[tote].nxt=head[u],head[u]=tote; }//左边点连右边点,左右边点均为下标1开始
bool SearchP(int n)
{
queue<int>q;
dis=inf;
memset(dx,-1,sizeof(dx)),memset(dy,-1,sizeof(dy));
repi(i,1,n)if(um[i]==-1) q.push(i),dx[i]=0;
while(!q.empty()){
int u=q.front(); q.pop();
if(dx[u]>dis) break;
reps(u)if(dy[v]==-1){
dy[v]=dx[u]+1;
if(vm[v]==-1) dis=dy[v];
else dx[vm[v]]=dy[v]+1,q.push(vm[v]);
}
}
return (dis!=inf);
}
bool dfs(int u)
{
reps(u)if(!vis[v]&&dy[v]==dx[u]+1){
vis[v]=1;
if(vm[v]!=-1&&dy[v]==dis) continue;
if(vm[v]==-1||dfs(vm[v])){
vm[v]=u,um[u]=v;
return true;
}
}
return false;
}
int cal(int n)//n为左边点数
{
int res=0;
memset(um,-1,sizeof(um)),memset(vm,-1,sizeof(vm));
while(SearchP(n)){
ms(vis);
repi(i,1,n) if(um[i]==-1&&dfs(i)) ++res;
}
return res;
}
}hk;
二分图多重匹配
左侧点匹配右侧点,但右侧点可被匹配多次
模板
/*
左侧只匹配一次,右侧匹配多次
若左侧多次不可左侧拆点后使用(不能限制拆除的点连的右侧点都不同),此时要用网络流
*/
struct Edge{int to,nxt;};
struct Bipartite_multi_matching{
int ul,ur;//左侧点数 右侧点数 下标均1开始
Edge e[MAX_E];
int head[MAX_N],tote;
void add_edge(int u,int v){e[++tote].to=v,e[tote].nxt=head[u];head[u]=tote;}
int num[MAX_N],match[MAX_N][MAX_N];//num 右侧每个点允许匹配数量,match[i][0]右侧每个点最终匹配数量,1-match[i][0]记录分别匹配了哪个点
bool used[MAX_N];
void init(int l,int r)
{
ul=l,ur=r;
repi(i,1,ul) head[i]=0;
}
bool dfs(int u)
{
reps(u)if(!used[v]){
used[v]=true;
if(match[v][0]<num[v]){
match[v][++match[v][0]]=u;
return true;
}
repi(i,1,match[v][0]){
if(dfs(match[v][i])){
match[v][i]=u;
return true;
}
}
}
return false;
}
int cal()
{
int res=0;
repi(i,1,ur) match[i][0]=0;
repi(i,1,ul){
repi(i,1,ur) used[i]=false;
if(dfs(i)) res++;
}
return res;
}
}bmm;
二分图最大权匹配
每条匹配边有权值,找权值和最大的匹配
O ( n 3 ) O(n^3) O(n3)
const ll inf=4e18;
struct Kuhn_Munkres{//最小权匹配则均加负权边(inf也是)
int n;//每侧点数(左右要相等)若左右不相等,少的一边加虚节点(n取左右两侧点最大值),连其它点都可以边权为0(直接初始化边权0)
ll g[MAX_N][MAX_N];//边权
ll lx[MAX_N],ly[MAX_N],slack[MAX_N];
int nxtx[MAX_N],nxty[MAX_N],visx[MAX_N],visy[MAX_N],pre[MAX_N],q[MAX_N],ql,qr;
void init(int _n)
{
n=_n;
repi(i,1,n)repi(j,1,n) g[i][j]=-inf;//不是要找完美匹配的话,设0,相应的注意nxtx,nxty时要判断是否有边
}
void add_edge(int u,int v,ll w){ g[u][v]=w; }//u左边点,v右边点,两侧点编号均为1-n
bool check(int now)
{
visy[now]=1;
if(nxty[now]!=-1) {
if(!visx[nxty[now]]) q[++qr]=nxty[now],visx[nxty[now]]=1;
return false;
}
while(now!=-1) swap(now,nxtx[nxty[now]=pre[now]]);
return true;
}
void bfs(int s)
{
repi(i,1,n) slack[i]=inf,visx[i]=visy[i]=false;
ms(visx),ms(visy);
q[ql=qr=1]=s;
visx[s]=qr=1;
ll d;
while(1){
while(ql<=qr){
int u=q[ql++];
repi(i,1,n)if(!visy[i]){
ll tmp=lx[u]+ly[i]-g[u][i];
if(slack[i]>=tmp){
slack[i]=tmp,pre[i]=u;
if(!slack[i]&&check(i)) return;
}
}
}
ll d=inf;
repi(i,1,n)if(!visy[i]&&slack[i]) d=min(d,slack[i]);
repi(i,1,n){
if(visx[i]) lx[i]-=d;
if(visy[i]) ly[i]+=d;
else slack[i]-=d;
}
ql=1,qr=0;
repi(i,1,n) if(!visy[i]&&!slack[i]&&check(i)) return;
}
}
ll cal()
{
repi(i,1,n) nxtx[i]=nxty[i]=-1,ly[i]=0;
repi(i,1,n) lx[i]=*max_element(g[i]+1,g[i]+1+n);
repi(i,1,n) bfs(i);
ll res=0;
repi(i,1,n) if(nxtx[i]!=-1) res+=g[i][nxtx[i]];//nxtx记录左边i连接右边哪个点,nxty记录右边i连左边哪个点
return res;
}
}km;
一般图匹配带花树算法,处理一般图的最大匹配
O ( n 3 ) O(n^3) O(n3)
int n,m;//点数 边数
bool G[MAX_N][MAX_N];
int Match[MAX_N],Count;//记录每个点与哪个点匹配,0为无匹配 count匹配数(匹配对数=匹配数/2)
bool InQueue[MAX_N],InPath[MAX_N],InBlossom[MAX_N];
int Head,Tail,Queue[MAX_N];
int Start,Finish,NewBase;
int Father[MAX_N],Base[MAX_N];
void CreateGraph()//建图
{
si(n),si(m);
repi(i,1,n)repi(j,1,n) G[i][j]=false;
repi(i,1,m){
int u,v; si(u),si(v);
G[u][v]=G[v][u]=true;
}
}
inline void Push(int u){ Queue[Tail++]=u,InQueue[u]=true; }
inline int Pop(){ return Queue[Head++]; }
int FindCommonAncestor(int u,int v)
{
repi(i,1,n) InPath[i]=false;
while(true)
{
u=Base[u],InPath[u]=true;
if(u==Start) break;
u=Father[Match[u]];
}
while(true)
{
v=Base[v];
if(InPath[v])break;
v=Father[Match[v]];
}
return v;
}
void ResetTrace(int u)
{
int v;
while(Base[u]!=NewBase)
{
v=Match[u];
InBlossom[Base[u]]=InBlossom[Base[v]]=true;
u=Father[v];
if(Base[u]!=NewBase) Father[u]=v;
}
}
void BloosomContract(int u,int v)
{
NewBase=FindCommonAncestor(u,v);
ms(InBlossom);
ResetTrace(u);
ResetTrace(v);
if(Base[u]!=NewBase) Father[u]=v;
if(Base[v]!=NewBase) Father[v]=u;
repi(tu,1,n)if(InBlossom[Base[tu]]){
Base[tu]=NewBase;
if(!InQueue[tu]) Push(tu);
}
}
void FindAugmentingPath()
{
repi(i,1,n) InQueue[i]=Father[i]=0;
repi(i,1,n) Base[i]=i;
Head=Tail=1;
Push(Start),Finish=0;
while(Head<Tail)
{
int u=Pop();
repi(v,1,n)if(G[u][v]&&(Base[u]!=Base[v])&&(Match[u]!=v)){
if((v==Start)||((Match[v]>0)&&Father[Match[v]]>0)) BloosomContract(u,v);
else if(Father[v]==0){
Father[v]=u;
if(Match[v]>0) Push(Match[v]);
else{ Finish=v;return; }
}
}
}
}
void AugmentPath()
{
int u,v,w;
u=Finish;
while(u>0)
{
v=Father[u],w=Match[v];
Match[v]=u,Match[u]=v;
u=w;
}
}
void Edmonds()
{
repi(i,1,n) Match[i]=0;
repi(u,1,n)if(Match[u]==0){
Start=u;
FindAugmentingPath();
if(Finish>0) AugmentPath();
}
}
void PrintMatch()
{
Count=0;
repi(u,1,n) if(Match[u]>0) Count++;
printf("%d\n",Count);
repi(u,1,n) printf("%d%c",Match[u],ce(u,n));
}
/*
CreateGraph();//建图
Edmonds();//进行匹配
PrintMatch();//输出匹配数和匹配
*/