二分图最大流(小白都能看懂系列2)

二分图最大流

前传

一个队讲一个算法,,,很不幸到我们队讲了,,可以说是看的最认真的算法了,这篇博客要好好写;
(更不幸的事发生了,,,摇色子我上去讲,难受的一批,硬着头皮讲完了)

二分图是啥咧??

二分图又称作二部图,是图论中的一种特殊模型。 设G=(V,E)是一个无向图,如果顶点V可分割为两个互不相交的子集(A,B),并且图中的每条边(i,j)所关联的两个顶点i和j分别属于这两个不同的顶点集(i in A,j in B),则称图G为一个二分图。

在这里插入图片描述
(二分图)上图中U和V构造的点集所形成的循环圈不为奇数,所以是二分图。

在这里插入图片描述
上图中U和V和W构造的点集所形成的的循环圈为奇数,所以不是二分图。(显而易见不是二分图)

二分图相关问题:

二分图最大匹配:匹配是二分图中边的集合,且集合中的任意边没有公共点,包含边数最多的匹配。

最小顶点覆盖:

在二分图中寻找一个尽量小的点集,使图中每一条边至少有一个点在该点集中。

最小顶点覆盖 == 最大匹配。

反证法证明:

假设当前存在一条两个端点都不在最小顶点覆盖点集中,那么这么光芒四射的边定可以增大最大匹配边集,与最大匹配矛盾,所以得证。

最小路径覆盖:

在二分图中寻找一个尽量小的边集,使图中每一个点都是该边集中某条边的端点。

最小路径覆盖 == 顶点数 - 最大匹配。

证明:因为一条边最多可以包含两个顶点,所以我们选边的时候让这样的边尽量多,也就是说最大匹配的边集数目咯。剩下的点就只能一个边连上一个点到集合里啦。

最大独立集:

在N个点中选出来一个最大点集,使这个点集中的任意两点之间都没有边。

最大独立集 == 顶点数 - 最大匹配。

证明:因为去掉最大匹配两端的顶点去掉以后,剩下的点肯定是独立集。我们再从每个匹配里面挑选出来一个点加入到独立集中,也是不会破坏原有独立集的独立性的。

BFS判断二分图code:

vector<int> G[maxn];  // 存边
int col[maxn];        // 标记顶点颜色
int n,m;         // 点和边的个数
 
bool bfs(){
  queue<int> q;
  q.push(1);     // 放入第一个点
  memset(col,0,sizeof(col));
  col[1] = 1;    // 先标记第一个点为1
  while(!q.empty()){
    int v = q.front();
    q.pop();
    for(int i=0;i<G[v].size();i++){
      int xx = G[v][i];
      if(col[xx] == 0){      // 判断这个点是否标记过
        col[xx] = -col[v];   // 没有标记过就标记上与v相反的颜色
        q.push(xx);
      }
      else{
        if(col[v] == col[xx]){    // 如果颜色冲突说明不是二分图
          return false;
        }
      }
    }`在这里插入代码片`
  }
  return true;
}

匈牙利算法,,很厉害,只是学了个大概

匈牙利算法几乎是二分图匹配的核心算法,除了二分图多重匹配外均可使用

匈牙利算法是由匈牙利数学家Edmonds于1965年提出,因而得名。匈牙利算法是基于Hall定理中充分性证明的思想,它是部图匹配最常见的算法,该算法的核心就是寻找增广路径,它是一种用增广路径求二分图最大匹配的算法。

增广路径

增广路径的定义:设M为二分图G已匹配边的集合,若P是图G中一条连通两个未匹配顶点的路径(P的起点在X部,终点在Y部,反之亦可),并且属M的边和不属M的边(即已匹配和待匹配的边)在P上交替出现,则称P为相对于M的一条增广路径。
增广路径是一条“交错轨”。也就是说, 它的第一条边是目前还没有参与匹配的,第二条边参与了匹配,第三条边没有…最后一条边没有参与匹配,并且起点和终点还没有被选择过,这样交错进行,显然P有奇数条

**

算法轮廓:

**

置M为空
找出一条增广路径P,通过取反操作获得更大的匹配M’代替M
重复2操作直到找不出增广路径为止
找增广路径的算法

我们采用DFS的办法找一条增广路径:
从X部一个未匹配的顶点u开始,找一个未访问的邻接点v(v一定是Y部顶点)。对于v,分两种情况:

如果v未匹配,则已经找到一条增广路
如果v已经匹配,则取出v的匹配顶点w(w一定是X部顶点),边(w,v)目前是匹配的,根据“取反”的想法,要将(w,v)改为未匹配,(u,v)设为匹配,能实现这一点的条件是看从w为起点能否新找到一条增广路径P’。如果行,则u-v-P’就是一条以u为起点的增广路径。
cx[i]表示与X部i点匹配的Y部顶点编号
cy[i]表示与Y部i点匹配的X部顶点编号`在这里插 /

//伪代码
bool dfs(int u)//寻找从u出发的增广路径
{
    for each v∈u的邻接点
        if(v未访问){
            标记v已访问;
            if(v未匹配||dfs(cy[v])){
                cx[u]=v;
                cy[v]=u; 
                return true;//有从u出发的增广路径
            }
        }
    return false;//无法找到从u出发的增广路径
}
//代码
bool dfs(int u){
    for(int v=1;v<=m;v++)
        if(t[u][v]&&!vis[v]){
            vis[v]=1;
            if(cy[v]==-1||dfs(cy[v])){
                cx[u]=v;cy[v]=u;
                return 1;
            }
        }
    return 0;
}
void maxmatch()//匈牙利算法主函数
{
    int ans=0;
    memset(cx,0xff,sizeof cx);
    memset(cy,0xff,sizeof cy);
    for(int i=0;i<=nx;i++) 
        if(cx[i]==-1)//如果i未匹配
        { 
            memset(visit,false,sizeof(visit)) ; 
            ans += dfs(i); 
        }
    return ans ;
} 

	 (整的明明白白的!!!!!看我博客)
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值