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

匈牙利算法

1. 二分图

二分图: 又称作二部图,是图论中一种特殊模型。设G=(V,E)是一个无向图,如果顶点V可分割为两个互不相交的子集(A,B),并且图中每条边所关联的两个顶点 i 和 j 分别属于这两个不同的顶点集( i ∈ A , j ∈ B i \in A,j \in B iA,jB),则称图G为一个二分图。

简单来说,如果图中所有顶点可以被分为两个集合,图中所有的边的头和尾不属于同一个顶点集合,而是跨越两个集合,则这个图是一个二分图。

二分图当且仅当图中不含奇数环

  • 如何判断二分图?染色法

    // 判断从某个节点出发,是否所有连通节点都满足相邻节点不同颜色
    bool dfs(std::vector<std::vector<int>> &graph, std::vector<int> &st, int i, int color)
    {
        // 之前被染色的情况
        if (st[i])
        {
            if (st[i] == color) return true;
            else return false;  // st[i] != color 矛盾
        }
        
        st[i] = color;
        
        for (int j: graph[i])
        {
            // 颜色1、2之间的切换可以用3-color
            if(!dfs(graph, st, j, 3 - color)) return false;
        }
        return true;
    }
    
    // 检查邻接表graph是否为二分图
    bool check(std::vector<std::vector<int>> &graph)
    {
        int n = graph.size();
        std::vector<int> st(n, 0);		// st: 标记染色,0(未染色),1和2为相邻节点颜色。
        bool ans = true;
        
        for (int i = 0; i < n; i ++)
        {
            if (!st[i] && !dfs(graph, st, i, 1))
            {
                ans = false;
                break;
            }
        }
        return ans;
    }
    
    import sys
    # 设置递归栈次数
    sys.setrecursionlimit(100000)
    
    def dfs(graph, st, i, color):
        if st[i]:
            if st[i] == color:
                return True
            else:
                return False
        st[i] = color
        
        for j in graph[i]:
            if not dfs(graph, st, j, 3 - color):
                return False
        
        return True
    
    def check(graph):
        n = len(graph)
        st = [0 for _ in range(n)]
        ret = True
        
        for i in range(n):
            if not st[i] and not dfs(graph, st, i, 1):
                ret = False
                break
        return ret
    
2. 匹配
  • **匹配:**在图论中,一个匹配(matching)是指一个边的集合,其中任意两条边都没有公共顶点。
  • **最大匹配:**一个图所有匹配中,所含匹配边数最多的匹配,称为这个图的最大匹配。
  • **完美匹配:**如果一个图的某个匹配中,所有的顶点都是匹配点,那么它就是一个完美匹配。完美匹配一定是最大匹配,但并非每个图都存在完美匹配。
3. 路径
  • **交替路径:**从一个未匹配点出发,依次经过非匹配边、匹配边、非匹配边…形成的路径称为交替路径。
  • **增广路径:**从一个未匹配点出发,走交替路,如果途径另一个未匹配点(出发的点不算),则这条交替路称为增广路(agumenting path)。
  • 增广路径的性质:
    • P的路径长度必定为奇数,第一条边和最后一条边都不属于M,因为两个端点分属两个集合,且未匹配。
    • P经过取反操作可以得到一个更大的匹配M’。
    • M为G的最大匹配当且仅当不存在相对于M的增广路径。
4. 匈牙利算法
  • **匈牙利算法:**利用增广路径求二分图的最大匹配算法称作匈牙利算法。(匈牙利数学家Edmonds于1965年提出)。
  • **基本思想:**通过寻找增广路径,把增广路径中的匹配边和非匹配边的相互交换,这样就会多出一条匹配边,直到找不到增广路径为止。
// find: 对于集合1的点i,找集合2的匹配对象,成功返回true
// graph: 邻接表
// match: 记录集合2的匹配对象,没有匹配为-1
bool find(std::vector<std::vector<int>> &graph, std::vector<int> &macth, std::vector<bool> &st, int i)
{
    
    for (int j: graph[i])
    {
        // 已经访问了集合2的对象j,那么跳过
        if (st[j]) continue;
        st[j] = true;
        // j没有被匹配或者j之前的匹配对象macth[j]可以另外被配对
        if (macth[j] == -1 || find(graph, macth, st, macth[j]))
        {
            macth[j] = i;
            return true;
        }
    }
    return false;
}
// hungarian: 求二分图最大匹配,返回最大匹配数量
// graph: 邻接表
// n1: 集合1大小
// n2: 集合2大小
int hungarian(std::vector<std::vector<int>> &graph, int n1, int n2)
{
    int ans = 0;
   	// match: 记录集合2的匹配对象,没有匹配为-1
    std::vector<int> macth(n2, -1);
    
    for (int i = 0; i < n1; i ++)
    {
        // st: 记录每次查找是否访问了集合2中的对象
        std::vector<bool> st(n1, false);
        if (find(graph, macth, st, i)) ans ++;
    }
    return ans;
}
def find(graph, macth, st, i):
    for j in graph[i]:
        if st[j]:
            continue
        
        st[j] = True
        if macth[j] == -1 or find(graph, macth, st, macth[j]):
            macth[j] = i
            return True
    return False
    
def hungarian(graph, n1, n2):
    ans = 0
    match = [-1 for _ in range(n2)]
    
    for i in range(n1):
        st = [False for _ in range(n2)]    
        if find(graph, match, st, i):
            ans += 1
        
    return ans
5. 算法应用

SORT算法原理(匈牙利匹配+卡尔曼滤波跟踪)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值