二分图的匹配 匈牙利算法 总结

 

二分图:一张无向图的N个节点,可以分成两个A、B两个非空集合,其中A∩B=∅,并且在统一集合内的点没有都没有边相连,那边称这张无向图为一张二分图。A、B分别称为二分图的左部和右部。

 

 

二分图(这一段是从另一个博客copy过来)

一、基本概念(自认为了解了概念,应该对遇到用二分匹配的题目更敏感)

二分图:其所有顶点可以分成两个集合X和Y,在同一集合中的点都不相连,所有的边关联在两个顶点中,恰好一个属于集合X,另一个属于集合Y。

二分图匹配:在图论中,一个「匹配」(matching)是一个边的集合,其中任意两条边都没有公共顶点。

最大匹配:一个图所有匹配中,所含匹配边数最多的匹配,称为这个图的最大匹配。

完美匹配:如果一个图的某个匹配中,所有的顶点都是匹配点,那么它就是一个完美匹配。完美匹配一定是最大匹配但并非每个图都存在完美匹配。

最小顶点覆盖:在顶点集合中,选取一部分顶点,这些顶点能够把所有的边都覆盖了。在所有的顶点覆盖集中,顶点数最小的那个叫最小顶点集合。

路径覆盖:在图中找一些路径,这些路径覆盖图中所有的顶点,每个顶点都只与一条路径相关联。

独立集:在所有的顶点中选取一些顶点,这些顶点两两之间没有连线,这些点就叫独立集

二、几个关系

最大匹配 = 最小顶点覆盖

最大独立集  = 最小路径覆盖 =总节点数 – 最小顶点覆盖(最大匹配)

最小路径覆盖 = 总节点数 – 最小顶点覆盖(最大匹配) 的一种解释:如果无匹配,需要n条路径才能覆盖所有点,两个点匹配意味着将可以把它们用一条路径覆盖,路径数就可以减1

 

弱鸡之前写过一道关于二分匹配的模板题,题意大概是给一张图,输入每个点代表的数字(0或1),然后问怎么转移,使主对角线上的元素全为1;

copy一下代码(就是板子题),找1,然后判断1是否在对角线上,以行为A集合内的点,列为B集合内的点,二分匹配

 1 #include 
 2 #include <string.h>
 3 #include <string>
 4 #include 
 5 #include 
 6 #include 
 7 using namespace std;
 8 const int maxen=110;
 9 int map[maxen][maxen],use[maxen],mark[maxen],n;
10 bool find(int x){
11        for(int i=1;i<=n;++i){
12            if(!mark[i]&&map[x][i]){ //未访问的,且为1的 
13                mark[i]=1;
14                if(!use[i]||find(use[i])){ //挑选未匹配点进行搜寻,若已经匹配了,找一下是否可以找一条新的增广路. 
15                    use[i]=x;
16                    return true;
17                }
18            }
19        }
20        return false;
21 }
22 int main(void){
23     int a[maxen],b[maxen];
24     while(scanf("%d",&n)!=EOF){
25         memset(map,0,sizeof(map));
26         for(int i=1;i<=n;++i){
27             for(int j=1;j<=n;++j){
28                 scanf("%d",&map[i][j]);
29             }
30         }
31         memset(use,0,sizeof(use));
32         int ans=0;
33         for(int i=1;i<=n;++i){
34             memset(mark,0,sizeof(mark));
35             if(find(i)){
36             ans++;                 //求出最大匹配; 
37         }
38         }
39         if(ans<n){
40             printf("-1\n");continue;
41         }
42         int j=0; 
43         for(int i=1;i<=n;++i){
44             while(use[i]!=i){  //当行与列不匹配时 
45                 a[j]=i; //记录要交换的行 
46                 b[j]=use[i];//记录要交换的列 
47                 swap(use[a[j]],use[b[j]]);   //交换匹配,使得行列能够匹配 
48                 j++;//匹配次数加一 
49             }
50         }
51         printf("%d\n",j);
52         for(int i=0;ii){
53             printf("C %d %d\n",a[i],b[i]);
54         }
55     }
56     return 0;
57 }

 

 

今天在队长的督促下,写题又遇到了二分匹配,之前虽然学了但很羞愧的没想到这种方法,这也是一道模板题。

POJ3041

 

Description

Bessie wants to navigate her spaceship through a dangerous asteroid field in the shape of an N x N grid (1 <= N <= 500). The grid contains K asteroids (1 <= K <= 10,000), which are conveniently located at the lattice points of the grid. 

Fortunately, Bessie has a powerful weapon that can vaporize all the asteroids in any given row or column of the grid with a single shot.This weapon is quite expensive, so she wishes to use it sparingly.Given the location of all the asteroids in the field, find the minimum number of shots Bessie needs to fire to eliminate all of the asteroids.

Input

* Line 1: Two integers N and K, separated by a single space. 
* Lines 2..K+1: Each line contains two space-separated integers R and C (1 <= R, C <= N) denoting the row and column coordinates of an asteroid, respectively.

Output

* Line 1: The integer representing the minimum number of times Bessie must shoot.

Sample Input

3 4
1 1
1 3
2 2
3 2

Sample Output

2

Hint

INPUT DETAILS: 
The following diagram represents the data, where "X" is an asteroid and "." is empty space: 
X.X 
.X. 
.X.
 


OUTPUT DETAILS: 
Bessie may fire across row 1 to destroy the asteroids at (1,1) and (1,3), and then she may fire down column 2 to destroy the asteroids at (2,2) and (3,2).
大意就是一张图上有几个点(输入给了)有目标攻击点,打一枪就可以把一行或一列上所有目标点消掉,问最少打几枪。
其实就是将行列分为两个集合,本质就是最小顶点覆盖,问需要几个顶点(这个点看别人的博客好久才get到,惭愧...),而最小顶点覆盖就是最大匹配啦,那就用上体的模板就好了;
  
#include 
#include <string.h>
#include <string>
#include 
#include 
using namespace std;
const int maxen=510;
int map[maxen][maxen],mark[maxen],use[maxen],N,K,R,C;
bool find(int x){
    for(int i=1;i<=N;++i){
        if(!mark[i]&&map[x][i]){
            mark[i]=1;
            if(!use[i]||find(use[i])){
                use[i]=x;
                return true;
            }
        }
    }
    return false;
}
int main(void){
    
    scanf("%d %d",&N,&K);
    for(int i=1;i<=K;++i){
        scanf("%d %d",&R,&C);
        map[R][C]=1;
    }
    memset(use,0,sizeof(use));
    int ans=0;
    for(int i=1;i<=N;++i){
        memset(mark,0,sizeof(mark));
        if(find(i))
        ++ans;
    }
    printf("%d\n",ans); 
    return 0;
}

这题其实上一题更加模板、、、

写了两道二分匹配的题,对这个算法算是有一定了解了,希望以后做题过程中能快点产生敏感性,不要再傻乎乎的有块好啃的肉都啃不下来..

                                                                                                                                                                                                          2019.2.26 凌晨.0.51

                                                                                                                                                                                                             快撑不住了的猴子

转载于:https://www.cnblogs.com/pipihoudewo/p/10434919.html

你可能感兴趣的:(二分图的匹配 匈牙利算法 总结)