求解分配问题(二) 二分图最大匹配算法

article/2025/9/17 9:30:24

我的前一篇文章介绍了对于分配问题的Kuhn-Munkre算法,该算法其实可以看作是邻接矩阵形式的匈牙利算法,如果更抽象地看这个算法,它可以看成是一个二分图匹配算法的变体算法,具体的说,是二分图最大权重匹配算法。我打算也把二分图最大权重匹配算法也介绍下,不过最大权重匹配算法又是基于最大匹配算法设计的,所以这篇文章先介绍二分图的最大匹配算法

二分图最大匹配问题

分配问题从图论的角度看其实是一个二分图(Bipartite Graph),就是说图中有两个点集,在各自的点集内的点互相没有连接,两个点集间的点可以连接,每个点存在多个允许的连接,如下图所示:
在这里插入图片描述

如果我们对二分图中的每个点,都确定最多只有一条连接,那么称之为该二分图的一个匹配,例如:
在这里插入图片描述
如果每个点都有一条线确定,即匹配的线数正好等于单个点集点数 n n n,那么称之为完美匹配,如下图:
在这里插入图片描述
对应于现实问题,我们当然希望尽量达到完美匹配,但是因为点的可选连线情况可能导致无论如何也达不到完美匹配,所以自然而然产生了最大匹配问题(Max-Bipartite Matching),最大可以找到多少条匹配线

用数学语言描述:

  • G = ( V , E ) G=(V,E) G=(V,E)是一个二分图, n = ∣ V ∣ n=|V| n=V m = ∣ E ∣ m=|E| m=E δ ( v ) , v ∈ V \delta(v), v\in V δ(v),vV表示一端连接到点 v v v的所有边, x e ∈ { 0 , 1 } x_e\in \{0,1\} xe{0,1}表示边 e e e是否被选中,则有最大匹配问题:
    m a x ∑ e ∈ E x e s . t . ∑ e ∈ δ ( v ) ≤ 1 , ∀ v ∈ V x e ∈ { 0 , 1 } , ∀ e ∈ E \begin{aligned} max\quad& \sum_{e\in E} x_e\\ s.t.\quad& \sum_{e\in \delta(v)}\leq 1,\forall v \in V \\ & x_e\in \{0,1\},\forall e \in E \\ \end{aligned} maxs.t.eExeeδ(v)1,vVxe{0,1},eE

算法主流程

在介绍算法前,先定义一些专用的算法术语,这些术语是算法的必备要素

  • 自由顶点(free vertex):就是指当前匹配结果下没有任何连线的点
  • 交替路径(alternating path): 由匹配边和非匹配边交替构成的一条路径,例如下面的图,加重的边表示已经在当前匹配中确定的边,细线表示还没确定的边
    在这里插入图片描述
  • 增广路径(augmenting path):属于交替路径,不过它的首末顶点都是自由顶点,即开头和最后的两条边必须是非匹配边,如下图所示
    在这里插入图片描述

我们观察一下增广路径,它是一条匹配边接一条非匹配的构造,并且首末两条边都是未确定的,如果我们把这个路径"翻转"一下,把匹配边变成非匹配,非匹配变为匹配,那么可以发现,这条路径上的匹配数会多出1:
在这里插入图片描述
这就给了我们启发,只要我们能在当前匹配的二分图中找到一条增广路径,那么就能扩充1数量的匹配;从数学理论上看也确实如此,最大匹配算法的理论前提就是:

  • 对于一个二分图,一个匹配 M M M是最大的,当且仅当图 G G G M M M的基础上找不到任何增广路径

自然地,我们就得到了算法的主流程:

  1. 初始化匹配 M M M M = ∅ M=\empty M=
  2. 在当前匹配的基础上,在图 G G G中搜索增广路径 P P P
  3. 如果存在 P P P,对增广路径进行翻转更新匹配 M M M,回到步骤2
  4. 输出 M M M

搜索增广路径

但是怎么搜索增广路径还是需要一个专门的子算法来处理;搜索过程一定是从一个自由顶点开始,到一个自由顶点结束,并且匹配边与非匹配边交替进行;这里介绍一种把匹配图转化成有向图的深度优先路径搜索算法:新增一个首节点 s s s和一个末结点 t t t,对于当前二分图中未确定的边,我们将其方向置为 X X X Y Y Y,对于匹配边,将方向置为 Y Y Y X X X;对于 X X X中未匹配的点,我们添加从 s s s到该点的边;对于 Y Y Y中未匹配的点,我们添加从该点到 t t t的边;这样二分图变成了一个有向图,我们采用深度优先遍历搜索从 s s s t t t的最短路径,如果能搜索到一条路径,那么去掉首末结点后就是一条增广路径

举个例子,当前二分图的匹配如下图所示:
在这里插入图片描述
将其转化成一个有向图:
在这里插入图片描述
然后我们就可以搜索到一条最短路径 s → x 0 → y 0 → x 1 → y 1 → t s\rightarrow x_0 \rightarrow y_0 \rightarrow x_1 \rightarrow y_1 \rightarrow t sx0y0x1y1t,去掉 s s s t t t,就是一条增广路径

代码实现

我写了一段python代码来实现上述的算法过程。定义了MaxMatchAlgorithm类,它只需要一个边矩阵作为输入,矩阵的行列分别表示二分图的 X X X Y Y Y,矩阵的每个元素表示该边是否可选;maxMatch实现了算法的主流程,getAugmentingPath方法用于搜索增广路径,invert方法则用于翻转增广路径

import numpy as npclass MaxMatchAlgorithm:def __init__(self, edges):'''edges : Matrix with num_x X num_y size'''self.num_x=edges.shape[0]self.num_y=edges.shape[1]self.edges=edgesdef maxMatch(self):matches=[]iter=1while True:print("######## iteration "+str(iter)+" ##############")print("...")print("...")iter+=1augPath=self.getAugmentingPath(matches)if len(augPath)==0:breakmatches=self.invert(augPath,matches) return matchesdef invert(self, augPath, matches):for i in range(len(augPath)):if i%2==0:if augPath[i][2]:matches.append((augPath[i][0],augPath[i][1]))else:matches.append((augPath[i][1],augPath[i][0]))else:removeIndex=-1for index in range(len(matches)):if (matches[index][0]==augPath[i][0] and matches[index][1]==augPath[i][1]) or (matches[index][0]==augPath[i][1] and matches[index][1]==augPath[i][0]):removeIndex=indexbreakmatches.pop(removeIndex)return matchesdef getAugmentingPath(self,matches):#directMatrix[i,j]=1 : x_i -> y_jtotalVertexN=self.num_x+self.num_y+2directMatrix=np.zeros((totalVertexN,totalVertexN))for i in range(0,totalVertexN-1):for j in range(0,totalVertexN):if i==0 and j>=1 and j<=self.num_x:directMatrix[i,j]=1continueif j==totalVertexN-1 and i>self.num_x:directMatrix[i,j]=1continueif i==j or i==0 or j==0 or j==totalVertexN-1:continueiIsX=FalsejIsX=Falseif i>0 and i<=self.num_x:iIsX=Trueelif i>self.num_x and i<=totalVertexN-2:iIsX=Falseif j>0 and j<=self.num_x:jIsX=Trueelif j>self.num_x and j<=totalVertexN-2:jIsX=Falseif iIsX and jIsX==False:directMatrix[i,j]=self.edges[i-1,j-1-self.num_x]for (x,y) in matches:directMatrix[x+1,y+1+self.num_x]=0directMatrix[y+1+self.num_x,x+1]=1for j in range(1, self.num_x+1):isMatched=Falsefor k in range(self.num_x, totalVertexN-1):if directMatrix[k][j]==1:isMatched=Truebreakif isMatched:directMatrix[0][j]=0for i in range(self.num_x, totalVertexN-1):isMatched=Falsefor k in range(1, self.num_x+1):if directMatrix[i][k]==1:isMatched=Truebreakif isMatched:directMatrix[i][totalVertexN-1]=0shortestRoute=self.findRoute(directMatrix,[0],[])augmentingPath=[]for i in range(1,len(shortestRoute)-2):vertexIndex_A=shortestRoute[i]actualIndex_A=0vertexIsX_A=Falseif vertexIndex_A>=1 and vertexIndex_A<=self.num_x:actualIndex_A=vertexIndex_A-1vertexIsX_A=Trueelse:actualIndex_A=vertexIndex_A-1-self.num_xvertexIndex_B=shortestRoute[i+1]actualIndex_B=0vertexIsX_B=Falseif vertexIndex_B>=1 and vertexIndex_B<=self.num_x:actualIndex_B=vertexIndex_B-1vertexIsX_B=Trueelse:actualIndex_B=vertexIndex_B-1-self.num_xaugmentingPath.append((actualIndex_A,actualIndex_B,vertexIsX_A))return augmentingPathdef findRoute(self, directMatrix, currentRoute, currentShortestRoute):if currentRoute[-1]==directMatrix.shape[0]-1:if len(currentShortestRoute)==0 or len(currentShortestRoute)>len(currentRoute):currentShortestRoute=currentRoute.copy()return currentShortestRoutefor i in range(0,directMatrix.shape[0]):if directMatrix[currentRoute[-1],i]==1:nextRoute=currentRoute.copy()nextRoute.append(i)route=self.findRoute(directMatrix, nextRoute,currentShortestRoute)if len(currentShortestRoute)==0 or len(currentShortestRoute)>len(route):currentShortestRoute=route.copy()return currentShortestRoute

用下面的代码测试,输入数据就是上面的例子:

import numpy as np
from MaxMatchAlgorithm import MaxMatchAlgorithm  edges=np.array([[1,0,1],[1,1,1],[0,0,1]
])maxMatchAlgorithm=MaxMatchAlgorithm(edges)
print(maxMatchAlgorithm.maxMatch())

结果
在这里插入图片描述
对应图上就是
在这里插入图片描述


http://chatgpt.dhexx.cn/article/qkcSCAcZ.shtml

相关文章

二分图最大匹配(匈牙利算法,Dinic网络流算法)

二分图最大匹配 二分图最大匹配问题: 有两个集合A,B,两个集合间有多条边连接集合中的点,且单个集合中的点各不相连,求两集合的点能两两配对的最大匹配数. (参考:)二分图最大匹配——匈牙利算法 匈牙利算法: A集合记录各点与B集合相连的点,B集合记录某点与A集合中匹配的点.遍历…

二分图最大匹配与最大独立集

一.概念部分 1.什么是二分图&#xff1f; 通俗的说法&#xff1a;就是可以把图分成两部分&#xff0c;每一部分任意两点之间没有关系&#xff08;同一部落&#xff09;&#xff0c;两部分之间点可能存在多种关系。 2.怎么判断二分图&#xff1f; &#xff08;1&#xff09;…

二分图最大匹配及最大权匹配

二分图最大匹配学习 一.二分图的基本知识二.二分图最大匹配什么是二分图最大匹配怎么求二分图最大匹配 三.二分图最大权匹配四.例题训练三.最小点覆盖数 一位大佬的神级解释 本以为有了网络流&#xff0c;就不用再学匈牙利了&#xff0c;但在做题的过程中&#xff0c;发现有些…

二分图最大匹配问题(匈牙利算法)

什么是二分图 如果一个无向图的的顶点可以分为两个互不相交的子集A和B&#xff0c;那么它就是二分图。也就是说&#xff0c;A、B内部不存在连边&#xff0c;所有连边都一头连着A中的顶点&#xff0c;另一头连着B中的顶点。 什么是二分图最大匹配&#xff1f; 二分图最大匹配…

二分图 二分图最大匹配

首先来说一下什么是二分图。 二分图 二分图又称作二部图&#xff0c;是图论中的一种特殊模型。 设G(V, E)是一个无向图。如果顶点集V可分割为两个互不相交的子集X和Y&#xff0c;并且图中每条边连接的两个顶点一个在X中&#xff0c;另一个在Y中&#xff0c;则称图G为二分图。…

二分图最大匹配(最大流)

先举个例子&#xff0c;有N台计算机和K个任务&#xff0c;每个计算机只能执行一个任务&#xff0c;但可以执行多种任务。现在给出N和K&#xff0c;和其关系&#xff0c;求出最多能处理的任务数。 这就是典型的二分图&#xff0c;整张图被分为两半&#xff0c;一半是电脑&#…

图论总结(一)二分图最大匹配

二分图最大匹配 (一)、二分图 1、定义2、性质3、判定(二)、二分图的匹配 1、二分图的最大匹配2、 Knig定理及其证明3、最小边覆盖与最大独立集(三)、增广路径 1、定义2、性质3、寻找增广路(四)、匈牙利算法 1、找增广路经的算法2、实践3、算法分析(五)、例题 1、最小…

二分图的最大匹配

一、概念&#xff1a; 二分图&#xff1a;简单来说&#xff0c;如果图中点可以被分为两组&#xff0c;并且使得所有边都跨越组的边界&#xff0c;则这就是一个二分图。准确地说&#xff1a;把一个图的顶点划分为两个不相交集 U 和V &#xff0c;使得每一条边都分别连接U、V中的…

二分图最大匹配问题

最近在做的打车项目中&#xff0c;涉及到了用户叫单后&#xff0c;将所有出单司机和所有订单匹配的问题&#xff0c;借此来学习一下二分图的匹配算法。 一、无权二分图最大匹配 首先要区分一下各个概念&#xff1a; 匹配&#xff1a;图G的一个匹配是由一组没有公共端点的不是…

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

二分图最大匹配 &#xff08;一&#xff09;、二分图的介绍1、定义2、充要条件 &#xff08;二&#xff09;、二分图的匹配1、二分图的最大匹配2、增广路径3、匈牙利算法&#xff08;1&#xff09;、复杂度&#xff08;2&#xff09;、算法思路&#xff08;3&#xff09;、代码…

B树最小高度和最大高度的推导

B树最小高度和最大高度的推导 对任意一棵包含n个关键字&#xff0c;高度为h&#xff0c;阶数为m的B树&#xff0c;其最小高度和最大高度的推导过程如下&#xff1a;

算法篇:树之树的高度

算法&#xff1a; 这一类题目很简单&#xff0c;不过却是树的最基本操作之一&#xff0c;引申为判断树是不是平衡二叉树。 一般做法是&#xff0c;计算二叉树的左右子树的高度1&#xff0c;然后取它们的最大值或者最小值。 题目1: https://leetcode-cn.com/problems/balanced-b…

【二叉树】最小高度树

0x00 题目 给定一个 有序 整数数组 元素各不相同且按 升序 排列 编写一个算法&#xff0c;创建一棵 高度最小 的二叉 搜索 树 给定有序数组: [-10,-3,0,5,9] 一个可能的答案是&#xff1a;[0,-3,9,-10,null,5] 它可以表示下面这个高度平衡二叉搜索树&#xff1a; 0 / \ -3 …

常见数据结构详细图解、树的高度、深度、层数、跳表、二叉搜索树、平衡二叉树、红黑树、B树、B+树

常见数据结构 常用的数据结构知识。 1.1 跳表 上图是一个有序链表&#xff0c;我们要检索一个数据就挨个遍历。如果想要再提升查询效率&#xff0c;可以变种为以下结构&#xff1a; 现在&#xff0c;我们要查询11&#xff0c;可以跳着来查询&#xff0c;从而加快查询速度。 …

树的高度 递归法和非递归法

递归法思路&#xff1a; 树的高度即节点子树的高度1&#xff08;节点子树的高度即左子树高度&#xff0c;右子树高度的最大值&#xff09; 代码如下&#xff1a; // Height_Recursive 递归法求树的高度 int Height_Recursive(TreeNode* pTree) {if (pTree NULL) {return 0;…

获取树高度的两种方法与完全二叉树的判断

树的高度 树的高度是节点高度的最大值。 每一层节点的高度如图所示。 方法一&#xff1a;递归 根节点的高度显然就是二叉树的高度。 /** * 获取树的高度* return*/ public int height(){return height2(root); }/*** 使用递归方法获取树的高度* param node* return*/ priv…

红黑树

一.简介 红黑树作为一种二叉搜索树的一种实现&#xff0c;红黑树的左右子树高度差可能大于 1。所以红黑树不是严格意义上的平衡二叉树&#xff08;AVL&#xff09;&#xff0c;但红黑树是黑色节点完美平衡&#xff0c; 其平均统计性能要强于 AVL 。 红黑树是每个节点都带有颜…

B树最大高度推导

文章目录 B树最大高度推导推导B树的最小高度推导最大高度 B树&#xff1a;MySQL数据库索引是如何实现的&#xff1f;1. 遇到问题2. 尝试用学过的数据结构解决这个问题3. 改造二叉查找树4. 索引的弊端 B树最大高度推导 【声明几个重要概念】 B树的关键字就是要查找的东西 B树…

红黑树详解

1&#xff0c;红黑树特点 每一个结点都有颜色&#xff0c;要么红色&#xff0c;要么黑色。根结点必须是黑色的。红色结点的子结点必须是黑色的。任何一个结点&#xff0c;到它所有叶子结点&#xff0c;经过相同个数的黑色结点。&#xff08;红黑树的平衡含义&#xff0c;左右高…

MYSQL的B+Tree索引树高度如何计算

我们使用MySQL数据库的时候&#xff0c;绝大部分的情况下在使用InnoDB存储引擎&#xff0c;偶尔会使用MyISAM存储引擎&#xff0c;至于其他存储引擎&#xff0c;我相信大家都很少接触到&#xff0c;甚至可能都没有听说过。所以本文只讲解InnoDB和MyISAM两个存储引擎的索引&…