二分匹配mark

来源:互联网 发布:js游戏引擎 编辑:程序博客网 时间:2024/05/29 13:17

二分图:一个无向图的顶点可以分割成两个互不相交的顶点集称为二分图。


若全部的点都匹配了称为完美匹配;



存在一条y1-x1-y2-x2(如图)的路径则称交替路径。

若路径的起点和终点未被匹配则存在一条增广路径。

则可使匹配数增加 1 ;



berge定理:若匹配M为最大匹配当且仅当二分图中不存在增广路径

原代码链接

/* **************************************************************************//二分图匹配(匈牙利算法的DFS实现)//初始化:g[][]两边顶点的划分情况//建立g[i][j]表示i->j的有向边就可以了,是左边向右边的匹配//g没有边相连则初始化为0//uN是匹配左边的顶点数,vN是匹配右边的顶点数//调用:res=hungary();输出最大匹配数//优点:适用于稠密图,DFS找增广路,实现简洁易于理解//时间复杂度:O(VE)//***************************************************************************///顶点编号从0开始的const int MAXN=510;int uN,vN;//u,v数目int g[MAXN][MAXN];int linker[MAXN];bool used[MAXN];bool dfs(int u)//从左边开始找增广路径{    int v;    for(v=0;v<vN;v++)//这个顶点编号从0开始,若要从1开始需要修改      if(g[u][v]&&!used[v])      {          used[v]=true;          if(linker[v]==-1||dfs(linker[v]))          {//找增广路,反向              linker[v]=u;              return true;          }      }    return false;//这个不要忘了,经常忘记这句}int hungary(){    int res=0;    int u;    memset(linker,-1,sizeof(linker));    for(u=0;u<uN;u++)    {        memset(used,0,sizeof(used));        if(dfs(u)) res++;    }    return res;}


/* *********************************************二分图匹配(Hopcroft-Carp的算法)。初始化:g[][]邻接矩阵调用:res=MaxMatch();  Nx,Ny要初始化!!!时间复杂大为 O(V^0.5 E) 适用于数据较大的二分匹配需要queue头文件********************************************** */const int MAXN=3000;const int INF=1<<28;int g[MAXN][MAXN],Mx[MAXN],My[MAXN],Nx,Ny;int dx[MAXN],dy[MAXN],dis;bool vst[MAXN];bool searchP(){    queue<int>Q;    dis=INF;    memset(dx,-1,sizeof(dx));    memset(dy,-1,sizeof(dy));    for(int i=0;i<Nx;i++)        if(Mx[i]==-1)        {            Q.push(i);            dx[i]=0;        }    while(!Q.empty())    {        int u=Q.front();        Q.pop();        if(dx[u]>dis)  break;        for(int v=0;v<Ny;v++)            if(g[u][v]&&dy[v]==-1)            {                dy[v]=dx[u]+1;                if(My[v]==-1)  dis=dy[v];                else                {                    dx[My[v]]=dy[v]+1;                    Q.push(My[v]);                }            }    }    return dis!=INF;}bool DFS(int u){    for(int v=0;v<Ny;v++)       if(!vst[v]&&g[u][v]&&dy[v]==dx[u]+1)       {           vst[v]=1;           if(My[v]!=-1&&dy[v]==dis) continue;           if(My[v]==-1||DFS(My[v]))           {               My[v]=u;               Mx[u]=v;               return 1;           }       }    return 0;}int MaxMatch(){    int res=0;    memset(Mx,-1,sizeof(Mx));    memset(My,-1,sizeof(My));    while(searchP())    {        memset(vst,0,sizeof(vst));        for(int i=0;i<Nx;i++)          if(Mx[i]==-1&&DFS(i))  res++;    }    return res;}




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

例题 HDU1151

题目大意:在一个城镇,有m个路口,和n条路,这些路都是单向的,而且路不会形成环,现在要弄一些伞兵去巡查这个城镇,伞兵只能沿着路的方向走,问最少需要多少伞兵才能把所有的路口搜一遍。

(其实就是给一个m个点n条边的有向无环图,求该图的最小路径覆盖)

#include<stdio.h>#include<string.h>#define N 202int use[N];   //记录y中节点是否使用int link[N];   //记录当前与y节点相连的x的节点int mat[N][N]; //记录连接x和y的边,如果i和j之间有边则为1,否则为0int gn,gm;    //二分图中x和y中点的数目int can(int t){    int j;    for(j=1;j<=gn;j++)    {       if(use[j]==0 && mat[t][j])//y中 第j个点未被使用.且有边       {           use[j]=1;//标记           if(link[j]==0 || can(link[j]))//如果j点没有连接则连接就t点           {                             //有连接则重新将连j的上一点link[j] 重新查找边、因为已经标记j点              link[j]=t;                //  link[j] 连接下一个相连的点;              return 1;           }       }    }    return 0;}int MaxMatch(){    int i,num;    num=0;    memset(link,0,sizeof(link));    for(i=1;i<=gn;i++)//x中 第i个点    {       memset(use,0,sizeof(use));       if(can(i)) num++;    }    return num;}void init(){memset(mat,0,sizeof(mat));}int main(){int t,k;scanf("%d",&t);while(t--){init();scanf("%d%d",&gn,&k);while(k--){int a,b;scanf("%d%d",&a,&b);mat[a][b]=1;}printf("%d\n",gn-MaxMatch());}return 0;}


HDU 1281

/*******************************************最大匹配数:匈牙利算法HDU 1281 车不能相互攻击则 坐标(x,y)上x只能放一个棋子,y上只能放一个棋子所以一个x对应着一个y,其他棋子不能放在x上或者y上.这样就是二分匹配了重要点:则是不可替代的点,所以拿去这个点看最大匹配数有无减少********************************************/#include<stdio.h>#include<string.h>#define N 202int use[N];   //记录y中节点是否使用int link[N];   //记录当前与y节点相连的x的节点int mat[N][N]; //记录连接x和y的边,如果i和j之间有边则为1,否则为0int gn,gm;    //二分图中x和y中点的数目int can(int t){    int j;    for(j=1;j<=gn;j++)    {       if(use[j]==0 && mat[t][j])//y中 第j个点未被使用.且有边       {           use[j]=1;//标记           if(link[j]==0 || can(link[j]))//如果j点没有连接则连接就t点           {                             //有连接则重新将连j的上一点link[j] 重新查找边、因为已经标记j点              link[j]=t;                //  link[j] 连接下一个相连的点;              return 1;           }       }    }    return 0;}int MaxMatch(){    int i,num;    num=0;    memset(link,0,sizeof(link));    for(i=1;i<=gn;i++)//x中 第i个点    {       memset(use,0,sizeof(use));       if(can(i)) num++;    }    return num;}void init(){memset(mat,0,sizeof(mat));}int main(){int n,m,k,cas=1,x,y;while(~scanf("%d%d%d",&n,&m,&k)){gn=n;gm=m;init();for(int i=0;i<k;i++){scanf("%d%d",&x,&y);mat[x][y]=1;}int ans=MaxMatch(),sum=0;for(int  i=1;i<=n;i++){for(int j=1;j<=m;j++){if(mat[i][j]){mat[i][j]=0;if(MaxMatch()!=ans)sum++;mat[i][j]=1;}}}printf("Board %d have %d important blanks for %d chessmen.\n",cas++,sum,ans);}return 0;}


HDU 1498

/*******************************************最大匹配数:匈牙利算法HDU 1498 题意:给出一个矩阵 一次可以消除一行或者一列的同种颜色的气球;如果可以在k次之内不能消完这种颜色的气球 则记录下;********************************************/#include<stdio.h>#include<string.h>#include<algorithm>using namespace std;#define N 202int use[N];   //记录y中节点是否使用int link[N];   //记录当前与y节点相连的x的节点int mat[N][N]; //记录连接x和y的边,如果i和j之间有边则为1,否则为0int gn,gm;    //二分图中x和y中点的数目int can(int t){    int j;    for(j=1;j<=gn;j++)    {       if(use[j]==0 && mat[t][j])//y中 第j个点未被使用.且有边       {           use[j]=1;//标记           if(link[j]==0 || can(link[j]))//如果j点没有连接则连接就t点           {                             //有连接则重新将连j的上一点link[j] 重新查找边、因为已经标记j点              link[j]=t;                //  link[j] 连接下一个相连的点;              return 1;           }       }    }    return 0;}int MaxMatch(){    int i,num;    num=0;    memset(link,0,sizeof(link));    for(i=1;i<=gn;i++)//x中 第i个点    {       memset(use,0,sizeof(use));       if(can(i))   num++;    }    return num;}void init(){memset(mat,0,sizeof(mat));}int main(){int n,k,i,j,l;int map[N][N];while(scanf("%d%d",&n,&k),n+k){int ans[N]={0},in[N*N+11],num1=0,num2=0;bool vis[N*N];memset(vis,false,sizeof(vis));memset(map,0,sizeof(map));for(i=1;i<=n;i++){for( j=1;j<=n;j++){scanf("%d",&map[i][j]);   //输入矩阵if(!vis[map[i][j]]){in[num1++]=map[i][j];   //记录出现的气球颜色并记录vis[map[i][j]]=true; }}}gn=gm=n;for(i=0;i<num1;i++){init();for(j=1;j<=n;j++)   //建二分图{for(l=1;l<=n;l++){if(map[j][l]==in[i])mat[j][l]=1;}}if(k<MaxMatch()) //如果匹配数大于k次则记录ans[num2++]=in[i];}sort(ans,ans+num2);//输出要排序if(num2==0)printf("-1\n");for(i=0;i<num2;i++)printf("%d%c",ans[i],i==num2-1?'\n':' ');}return 0;}


HDU 1507

/******************************************* 最大匹配数:匈牙利算法 HDU 1507题意:最大匹配给出一些不能放的格子 剩下的放置 2x1的格子问最大多少可以放几个2x1格子并输出********************************************/#include <stdio.h>#include <algorithm>#include <iostream>#include <string.h>#include <vector>using namespace std;const int MAXN = 510;int uN,vN;//u,v的数目,使用前面必须赋值int g[MAXN][MAXN];//邻接矩阵int linker[MAXN];bool used[MAXN];bool dfs(int u){    for(int v = 0; v < vN;v++)        if(g[u][v] && !used[v])        {            used[v] = true;            if(linker[v] == -1 || dfs(linker[v]))            {                linker[v] = u;                return true;            }        }return false;}int MaxMatch(){    int res = 0;    memset(linker,-1,sizeof(linker));    for(int u = 0;u < uN;u++){        memset(used,false,sizeof(used));        if(dfs(u))res++;    }    return res;}int a[110][110];int b[100];int main(){    int n,m,k;    int u,v;    while(scanf("%d%d",&n,&m),n+m)    {        scanf("%d",&k);        memset(a,0,sizeof(a));        while(k--){            scanf("%d%d",&u,&v);            u--,v--;            a[u][v]=-1;        }        int index = 0;        for(int i = 0;i < n;i++){ for(int j = 0;j < m;j++)if(a[i][j]!=-1){b[index] = i*m + j;a[i][j] = index++;}}uN = vN = index;memset(g,0,sizeof(g));for(int i = 0;i < n;i++){//建立匹配图for(int j= 0;j < m;j++){if(a[i][j]!=-1 && (i+j)%2==1){u = a[i][j];if(i > 0 && a[i-1][j]!=-1)g[u][a[i-1][j]]=1;if(i < n-1 && a[i+1][j]!=-1)g[u][a[i+1][j]]=1;if(j > 0 && a[i][j-1]!=-1)g[u][a[i][j-1]]=1;if(j < m-1 && a[i][j+1]!=-1)g[u][a[i][j+1]]=1;}}}int ans = MaxMatch();printf("%d\n",ans);for(int i = 0;i <vN;i++){if(linker[i]!=-1){int x1 = b[i]/m;int y1 = b[i]%m;int x2 = b[linker[i]]/m;int y2 = b[linker[i]]%m;printf("(%d,%d)--(%d,%d)\n",x1+1,y1+1,x2+1,y2+1);}}printf("\n");    }    return 0;}



0 0