二分匹配mark

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


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



存在一条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 202
int use[N];   //记录y中节点是否使用
int link[N];   //记录当前与y节点相连的x的节点
int mat[N][N]; //记录连接x和y的边,如果i和j之间有边则为1,否则为0
int 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 202
int use[N];   //记录y中节点是否使用
int link[N];   //记录当前与y节点相连的x的节点
int mat[N][N]; //记录连接x和y的边,如果i和j之间有边则为1,否则为0
int 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 202
int use[N];   //记录y中节点是否使用
int link[N];   //记录当前与y节点相连的x的节点
int mat[N][N]; //记录连接x和y的边,如果i和j之间有边则为1,否则为0
int 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;
}



转载于:https://www.cnblogs.com/kewowlo/p/4002599.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值