匈牙利算法(二分图)

                                                                                                            ---------------------------------------------------------------------题材大多来自网络,本篇由神犇整理

基本概念二分图

二分图:是图论中的一种特殊模型。若能将无向图G=(V,E)的顶点V划分为两个交集为空的顶点集,并且任意边的两个端点都分属于两个集合,则称图G为一个为二分图。

匹配:一个匹配即一个包含若干条边的集合,且其中任意两条边没有公共端点。如下图,图3的红边即为图2的一个匹配。

1 最大匹配
    在G的一个子图M中,M的边集中的任意两条边都不依附于同一个顶点,则称M是一个匹配。选择这样的边数最大的子集称为图的最大匹配问题,最大匹配的边数称为最大匹配数.如果一个匹配中,图中的每个顶点都和图中某条边相关联,则称此匹配为完全匹配,也称作完备匹配。如果在左右两边加上源汇点后,图G等价于一个网络流,最大匹配问题可以转为最大流的问题。解决此问的匈牙利算法的本质就是寻找最大流的增广路径。上图中的最大匹配如下图红边所示:

2 最优匹配

最优匹配又称为带权最大匹配,是指在带有权值边的二分图中,求一个匹配使得匹配边上的权值和最大。一般X和Y集合顶点个数相同,最优匹配也是一个完备匹配,即每个顶点都被匹配。如果个数不相等,可以通过补点加0边实现转化。一般使用KM算法解决该问题。

3 最小覆盖

二分图的最小覆盖分为最小顶点覆盖和最小路径覆盖:

①最小顶点覆盖是指最少的顶点数使得二分图G中的每条边都至少与其中一个点相关联,二分图的最小顶点覆盖数=二分图的最大匹配数;

②最小路径覆盖也称为最小边覆盖,是指用尽量少的不相交简单路径覆盖二分图中的所有顶点。二分图的最小路径覆盖数=|V|-二分图的最大匹配数;

4 最大独立集

最大独立集是指寻找一个点集,使得其中任意两点在图中无对应边。对于一般图来说,最大独立集是一个NP完全问题,对于二分图来说最大独立集=|V|-二分图的最大匹配数。如下图中黑色点即为一个最大独立集:

基本概念匈牙利算法

交替路:从一个未匹配点出发,依次经过非匹配边、匹配边、非匹配边...形成的路径叫交替路。*

增广路:从一个未匹配点出发,走交替路,如果途径另一个未匹配点(出发的点不算),则这条交替路称为增广路(agumenting path)。

二、最大匹配与最小点覆盖

最小点覆盖:假如选了一个点就相当于覆盖了以它为端点的所有边,你需要选择最少的点来覆盖所有的边

最小割定理是一个二分图中很重要的定理:一个二分图中的最大匹配数等于这个图中的最小点覆盖数。

最小点集覆盖==最大匹配。在这里解释一下原因,首先,最小点集覆盖一定>=最大匹配,因为假设最大匹配为n,那么我们就得到了n条互不相邻的边,光覆盖这些边就要用到n个点。现在我们来思考为什么最小点击覆盖一定<=最大匹配。任何一种n个点的最小点击覆盖,一定可以转化成一个n的最大匹配。因为最小点集覆盖中的每个点都能找到至少一条只有一个端点在点集中的边(如果找不到则说明该点所有的边的另外一个端点都被覆盖,所以该点则没必要被覆盖,和它在最小点集覆盖中相矛盾),只要每个端点都选择一个这样的边,就必然能转化为一个匹配数与点集覆盖的点数相等的匹配方案。所以最大匹配至少为最小点集覆盖数,即最小点击覆盖一定<=最大匹配。综上,二者相等。

三、匈牙利算法

由增广路的性质,增广路中的匹配边总是比未匹配边多一条,所以如果我们放弃一条增广路中的匹配边,选取未匹配边作为匹配边,则匹配的数量就会增加。匈牙利算法就是在不断寻找增广路,如果找不到增广路,就说明达到了最大匹配。

先给一个例子 
1、起始没有匹配

2、选中第一个x点找第一跟连线 

3、选中第二个点找第二跟连线 

4、发现x3的第一条边x3y1已经被人占了,找出x3出发的的交错路径x3-y1-x1-y4,把交错路中已在匹配上的边x1y1从匹配中去掉,剩余的边x3y1 x1y4加到匹配中去 

5、同理加入x4,x5。

匈牙利算法可以深度有限或者广度优先,刚才的示例是深度优先,即x3找y1,y1已经有匹配,则找交错路。若是广度优先,应为:x3找y1,y1有匹配,x3找y2。

算法模板(邻接表 & C++)

深度优先匈牙利算法代码

#define maxn 10//表示x集合和y集合中顶点的最大个数!
 int nx,ny;//x集合和y集合中顶点的个数
 int edge[maxn][maxn];//edge[i][j]为1表示ij可以匹配
 int cx[maxn],cy[maxn];//用来记录x集合中匹配的y元素是哪个!
 int visited[maxn];//用来记录该顶点是否被访问过!
 int path(int u)
 {
     int v;
     for(v=0;v<ny;v++)
     {
         if(edge[u][v]&&!visited[v])
         {
             visited[v]=1;
            if(cy[v]==-1||path(cy[v]))//如果y集合中的v元素没有匹配或者是v已经匹配,但是从cy[v]中能够找到一条增广路
             {
                 cx[u]=v;
                 cy[v]=u;
                 return 1;
             }
         }
     }
     return 0;
 }
 int maxmatch()
 {
     int res=0;
     memset(cx,0xff,sizeof(cx));//初始值为-1表示两个集合中都没有匹配的元素!
     memset(cy,0xff,sizeof(cy));
     for(int i=0;i<=nx;i++)
     {
         if(cx[i]==-1)
         {
             memset(visited,0,sizeof(visitited));
             res+=path(i);
         }
     }
     return res;
 }

四、相关POJ题目

(1) poj3041

题目意思就是一颗子弹可以干掉任意一行或一列的障碍,问最少需要花费多少子弹清除呢

也就是求最小点集覆盖。

#include<iostream>
#include<stdio.h>
#include<string.h>
#define Max 505
using namespace std;
int a[Max][Max];
int visit[Max];
int match[Max];
int N,K;
int path(int u)
{
    int v;
    for(v=1;v<=N;v++)
    {
          if(a[u][v] && !visit[v])
          {
                visit[v] = 1;
                if(match[v] == -1 || path(match[v]))
                {
                            match[v] = u;
                            return 1;
                }
          }
    }
    return 0;
}
int main()
{

      int i,j,k,count;
      scanf("%d %d",&N,&K);

      memset(a,0,sizeof(a));
      memset(match,-1,sizeof(match));
      count = 0;
      for(i=1;i<=K;i++)
      {
           scanf("%d %d",&j,&k);
           a[j][k] = 1;
      }

      for(i=1;i<=N;i++)
      {
             memset(visit,0,sizeof(visit));
             if(path(i))
               count++;
      }
      printf("%d\n",count);

      return 0;
}

(2) poj3020

此题重要的是做出图来,以什么样的观点作图。可以画 h*w -> h*w 的图,点i与上下左右四个点有边存在,求最大匹配,最后结果为 总点数 - 最大匹配/2

#include<iostream>
#include<stdio.h>
#include<string.h>
#define Max 520
using namespace std;
int a[Max][Max];
int visit[Max];
int match[Max];
int N;
char str[50][15];
int path(int u)
{
    int v;
    for(v=1;v<=N;v++)
    {
          if(a[u][v] && !visit[v])
          {
                visit[v] = 1;
                if(match[v] == -1 || path(match[v]))
                {
                            match[v] = u;
                            return 1;
                }
          }
    }
    return 0;
}
int Find()
{
    int count = 0;
    int i;
    for(i=1;i<=N;i++)
    {
             memset(visit,0,sizeof(visit));
             if(path(i))
               count++;
    }
    return count;
}
int init(int h,int w) {
    int ctr,i,j;
    int x,y;
    int s,d;
    ctr=0;
    for ( i=1;i<=h;i++ ) {
        for ( j=1;j<=w;j++ ) {
            if ( str[i][j]==‘*‘ ) {
                ctr++;
                x=i;
                y=j;
                s=(x-1)*w+y;
                if ( y+1<=w&&str[x][y+1]==‘*‘ ) {
                    d=(x-1)*w+y+1;
                    a[s][d]=1;
                }
                if ( x+1<=h&&str[x+1][y]==‘*‘ ) {
                    d=(x)*w+y;
                    a[s][d]=1;
                }
                if ( y-1>=1&&str[x][y-1]==‘*‘ ) {
                    d=(x-1)*w+y-1;
                    a[s][d]=1;
                }
                if ( x-1>=1&&str[x-1][y]==‘*‘ ) {
                    d=(x-2)*w+y;
                    a[s][d]=1;
                }
            }
        }
    }
    return ctr;
}
int main()
{

      int i,j,k,n,totalnum,matchnum,h,w;
      scanf("%d",&n);
      while(n--)
      {
          memset(a,0,sizeof(a));
          memset(match,-1,sizeof(match));

          scanf("%d %d",&h,&w);
          getchar();
          for(i=1;i<=h;i++)
          {
              for(j=1;j<=w;j++)
              {
                 scanf("%c",&str[i][j]);
              }
              getchar();
          }

          totalnum = init(h,w);
          N = h*w;
          matchnum = Find();

          printf("%d\n",totalnum - matchnum/2);

      }
      return 0;
}//二分图
时间: 2024-12-24 16:39:38

匈牙利算法(二分图)的相关文章

poj1274 匈牙利算法 二分图最大匹配

poj1274 题意: 有n个奶牛, m个畜舍, 每个畜舍最多装1头牛,每只奶牛只有在自己喜欢的畜舍里才能产奶. 求最大产奶量. 分析: 其实题意很明显, 二分图的最大匹配, 匈牙利算法. #include<iostream> #include<cstdio> #include<string.h> #include<cstring> using namespace std; int n, m, sum, v[210], ans[210], map1[210]

匈牙利算法 二分图最大匹配题模板

[任务] 给定一个二分图,用匈牙利算法求这个二分图的最大匹配数. [说明] 求最大匹配,那么我们希望每一个在左边的点都尽量找到右边的一个点和它匹配. 我们一次枚举左边的点x的所有出边指向的点y, 若y之前没有被匹配,那么(x,y)就是一对合法的匹配,我们将匹配数加一, 否则我们试图给原来匹配的y和x'重新找一个匹配,如果x'匹配成功,那么(x,y)就可以新增为一对合法的匹配. 给x'寻找匹配的过程可以递归解决. [接口] int hungary(); 复杂度O(|E|*sqrt(|V|)) 输入

Bzoj 1562: [NOI2009]变换序列 匈牙利算法,二分图匹配

题目: http://cojs.tk/cogs/problem/problem.php?pid=409 409. [NOI2009]变换序列 ★★☆   输入文件:transform.in   输出文件:transform.out   简单对比时间限制:1 s   内存限制:128 MB [问题描述] 对于N个整数0, 1, ……, N-1,一个变换序列T可以将i变成Ti,其中 定义x和y之间的距离.给定每个i和Ti之间的距离D(i,Ti), 你需要求出一个满足要求的变换序列T.如果有多个满足条

匈牙利算法(二分图匹配)

#include<iostream> #include<cstdio> #include<cstring> #include<cstdlib> #include<cmath> #include<algorithm> using namespace std; int n,m,k,ans,cnt,head[1001],vis[1001],match[1001]; struct uio{ int next,to; }edge[1000001

它处资料:二分图最大匹配的匈牙利算法

资料出处:点击打开链接 匈牙利算法 二分图最大匹配的匈牙利算法:  二分图是这样一个图,它的顶点能够分类两个集合X和Y,全部的边关联在两个顶点中.恰好一个属于集合X.还有一个属于集合Y. 最大匹配: 图中包括边数最多的匹配称为图的最大匹配. 完美匹配: 假设全部点都在匹配边上.称这个最大匹配是完美匹配. 最小覆盖: 最小覆盖要求用最少的点(X集合或Y集合的都行)让每条边都至少和当中一个点关联.能够证明:最少的点(即覆盖数)=最大匹配数 最小路径覆盖: 用尽量少的不相交简单路径覆盖有向无环图G的全

hdoj 2063 过山车(二分图匹配之匈牙利算法)

过山车 http://acm.hdu.edu.cn/showproblem.php?pid=2063 Time Limit: 1000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 13019    Accepted Submission(s): 5709 Problem Description RPG girls今天和大家一起去游乐场玩,终于可以坐上梦寐以求的过山车

匈牙利算法dfs模板 [二分图][二分图最大匹配]

最近学了二分图最大匹配,bfs模板却死活打不出来?我可能学了假的bfs 于是用到了dfs模板 寻找二分图最大匹配的算法是匈牙利算法 匈牙利算法的主要程序是寻找增广路 寻找增光路是过程是:从一个未经配对的点出发,历经未配边.匹配边.未配边.匹配边.未配边....最终到达一个未配点的过程,只要把路径中的未配边和匹配边的“身份”对调,匹配就加一了.这就是一个寻找增广路的过程,通过不断寻找增广路,可以找到最大的匹配. 1 #include<cstdio> 2 #include<cstring&g

FOJ 2232 匈牙利算法找二分图最大匹配

题目链接 简单理解匈牙利算法 简单理解二分图 尽量让每一个随从击败一个对手且随从全部存活,关键是为每一个随从找对手(递归过程),"腾". #include<iostream> #include<cstdio> #include<cstring> using namespace std; int used[110]; int g[110][110]; //建立随从和对手的对战关系 int ee[110]; int n; struct people{ i

&quot;《算法导论》之‘图’&quot;:不带权二分图最大匹配(匈牙利算法)

博文“二分图的最大匹配.完美匹配和匈牙利算法”对二分图相关的几个概念讲的特别形象,特别容易理解.本文介绍部分主要摘自此博文. 还有其他可参考博文: 趣写算法系列之--匈牙利算法 用于二分图匹配的匈牙利算法 1. 前言 二分图:简单来说,如果图中点可以被分为两组,并且使得所有边都跨越组的边界,则这就是一个二分图.准确地说:把一个图的顶点划分为两个不相交集 U 和V ,使得每一条边都分别连接U.V中的顶点.如果存在这样的划分,则此图为一个二分图.二分图的一个等价定义是:不含有「含奇数条边的环」的图.

算法练习系列—hiho1122二分图最大匹配之匈牙利算法

题目地址:http://hihocoder.com/problemset/problem/1122 该题目的关键是2个问题:1点用bfs构造二分图   2:针对二分图的其中S中的结点,遍历找增广路(匈牙利算法求二分图的最大匹配) 每找到一条增广路就多找到了一条匹配. 代码如下: /* 这题有两点需要注意:1点用bfs构造二分图 2:针对二分图的其中S中的结点,遍历找增广路(匈牙利算法求二分图的最大匹配) 每找到一条增广路 就多找到了一条匹配 */ #include <iostream> #in