二分图最大匹配

二分图最大匹配

增广路的性质:

1).长度是奇数

2).路径上第1,3,5...,len条边是非匹配边,第2,4,6,...,len-1条边是匹配边。

二分图的一组匹配S是最大匹配,当且仅当图中不存在S的增广路

相关定理:

完备匹配:给定一张二分图,其左部、右部节点数相同,均为N个节点。如果该二 分图的最大匹配包含N条匹配边,则称该二分图具有完备匹配。

多重匹配:给定一张包含N个左部节点、M个右部节点的二分图。从中选出尽量多 的边,使第i(1<=i<=N)个左部节点至多与kli条选出的边相连,第j个右部节点至 多与kri条选出的边相连。

kli=kri=1时,上述问题转化为二分图最大匹配问题

最大匹配数:最大匹配的匹配边的数目

最小点覆盖数:选取最小的点,使任意一条边至少有一个端点被选择

最大独立数:选取最多的点,使任意所选两点均不相连

最小路径覆盖数:对于一个DAG(有向无环图),选取最少条路径,使得每个顶点属于且仅属于一条路径。路径长可以为0(即单个点)。

最小路径可重复点覆盖:先对原有向图求传递闭包,然后在新的有向无环图上求最小路径覆盖。

定理1:最小点覆盖数=最大匹配数(Konig定理)

定理2:最大独立数=最大匹配数

定理3:最小路径覆盖数=顶点数-最大匹配数

匈牙利算法(增广路算法) O(NM)

P3386

# include <bits/stdc++.h>
using namespace std;

const int MAXN=1e3+100;
vector<int> G[MAXN];
int match[MAXN],used[MAXN];
int dfs(int u)
{
   used[u]=1;
   int len=G[u].size();
   for(int i=0;i<len;++i){
       int v=G[u][i],w=match[v];
       if(used[v]) continue;
       used[v]=1;
       if(w<0||(!used[w]&&dfs(w))){
           match[u]=v;
           match[v]=u;
           return 1;
      }
  }
   return 0;
}
int main()
{
   int N,M,E;
   scanf("%d%d%d",&N,&M,&E);
   for(int i=1;i<=E;++i){
       int u,v; scanf("%d%d",&u,&v);
       v+=N;
       G[u].push_back(v);
       G[v].push_back(u);
  }
   int ans=0;
   memset(match,-1,sizeof(match));
   for(int i=1;i<=N;++i){
       if(match[i]<0){
           memset(used,0,sizeof(used));
           if(dfs(i)) ans++;
      }
  }
   printf("%d\n",ans);

   return 0;
}

有向无环图的最小路径点覆盖

# include <bits/stdc++.h>
using namespace std;

const int MAXN=200+50;
int G[MAXN][MAXN];
int match[MAXN],vis[MAXN];
int N,M;
int succ[MAXN],hide[MAXN];
int dfs(int x)
{
   for(int i=1;i<=N;++i){
       if(G[x][i]&&!vis[i]){
           vis[i]=1;
           if(!match[i]||dfs(match[i])){
               match[i]=x;
               return 1;
          }
      }
  }
   return 0;
}
int main()
{
   scanf("%d%d",&N,&M);
   for(int i=1;i<=M;++i){
       int x,y; scanf("%d%d",&x,&y);
       G[x][y]=1;
  }
   for(int i=1;i<=N;++i) G[i][i]=1;
   for(int k=1;k<=N;++k){
       for(int i=1;i<=N;++i){
           for(int j=1;j<=N;++j){
               G[i][j]|=G[i][k]&&G[k][j];
          }
      }
  }
   for(int i=1;i<=N;++i) G[i][i]=0;
   int ans=N;
   for(int i=1;i<=N;++i){
       memset(vis,0,sizeof(vis));
       ans-=dfs(i);
  }
   printf("%d\n",ans);
   for(int i=1;i<=N;++i) succ[match[i]]=1;
   for(int i=1,k=0;i<=N;++i){
       if(!succ[i]) hide[++k]=i;
  }
   memset(vis,0,sizeof(vis));
   int modify=1;
   while(modify){
       modify=0;
       for(int i=1;i<=ans;++i){
           for(int j=1;j<=N;++j){
               if(G[hide[i]][j]) vis[j]=1;
          }
      }
       for(int i=1;i<=ans;++i){
           if(vis[hide[i]]){
               modify=1;
               while(vis[hide[i]]) hide[i]=match[hide[i]];
          }
      }
  }
   //for(int i=1;i<=ans;++i) printf("%d ",hide[i]);
   //printf("\n");

   return 0;
}



posted @ 2022-02-26 23:37  fengzlj  阅读(82)  评论(0)    收藏  举报