二部图匹配-匈牙利算法-KM算法

int M, N;            //M, N分别表示左、右侧集合的元素数量
int Map[MAXM][MAXN]; //邻接矩阵存图
int p[MAXN];         //记录当前右侧元素所对应的左侧元素
bool vis[MAXN];      //记录右侧元素是否已被访问过
bool match(int i)
{
    for (int j = 1; j <= N; ++j)
        if (Map[i][j] && !vis[j]) //有边且未访问
        {
            vis[j] = true;                 //记录状态为访问过
            if (p[j] == 0 || match(p[j])) //如果暂无匹配,或者原来匹配的左侧元素可以找到新的匹配
            {
                p[j] = i;    //当前左侧元素成为当前右侧元素的新匹配
                return true; //返回匹配成功
            }
        }
    return false; //循环结束,仍未找到匹配,返回匹配失败
}
int Hungarian()
{
    int cnt = 0;
    for (int i = 1; i <= M; ++i)
    {
        memset(vis, 0, sizeof(vis)); //重置vis数组
        if (match(i))
            cnt++;
    }
    return cnt;
}

匈牙利算法用于无权二部图匹配,求二部图的最多连接边数。中心思想是:

1,从 i 点开始出发,试探有没有孤立点 j 可以连接。

       1.1如果试探的 j 点是孤立点,那么直接连在一起,这样就新增加了一条边。新增加了一条匹配边之后,就退出match函数了。

       1.2如果试探的 j 点已经不是孤立点了,这意味着,实际上被试探的 j 点和其他的点匹配了。这里要做的,就是试着把 j 点让给 i 点。如果 j 点的原有匹配点可以和 j 点以外的点匹配成功,那么,j 点就可以“脱身”出来,和 i 点形成新的组合。这样做,不会损失原来的组合(因为已经为落单的点找到匹配对象了),同时也增加了新的一条边。如果 j点的原有匹配点和 j 点以外的点不能匹配成功,那么即使拆分原有组合,把 j 和 i 组合在一起,也损失了一条原有边,即使把i,j连一起多了一条边,总体来看没有收益。

2,为什么每试探成功一次,就加一呢?因为每次即使试探成功,也只会增加一条匹配边。

我感觉论证不需要结合交替路就能理解。代码的意思和直接构造交替路还是有一点差别的。


KM算法

KM算法用于带权二部图匹配,求二部图最优(大)匹配权重和。中心思想是基于贪心构造子图,如果能在子图上找出完全匹配的路径,那么这份路径的权重和就是整个二部图最优匹配的权重和。子图找完全匹配路径,就可以用上面的匈牙利算法来解了。什么是完全匹配呢?就是二部图的少端点侧,所有点都找到了另一端的匹配点。万一原图中有些端点就是孤立的呢?别担心,可以看成边的权重为0。更具体的讲解看 从匈牙利算法到KM算法 - 知乎,讲得非常清晰了。

注意:KM算法需要从少端点侧出发试探,逐一匹配多端点侧。否则会死循环,失败。

class KM_Algorithm:
    def __init__(self, Net):
        #Net = [[3,4,6,4,9],[6,4,5,3,8],[7,5,3,4,2],[6,3,2,2,5],[8,4,5,4,7]]  #NGraphic net
        #self.Net = [[2,3,0,0],[0,4,4,0],[5,6,0,0],[0,0,7,0]]
        #Net = [[1,1,0,0],[0,1,1,0],[1,1,0,0],[0,0,1,0]]
        #Net = [[2,1,1],[3,2,1],[1,1,1]]
        self.Net = Net
        self.x_number = len(self.Net)
        self.y_number = len(self.Net[0])
        self.ux, self.uy = np.zeros(self.x_number, dtype=int), np.zeros(self.y_number, dtype=int) # variable for record path
        self.lx, self.ly = np.zeros(self.x_number, dtype=int), np.zeros(self.y_number, dtype=int) # sign
        self.result = np.zeros(self.y_number, dtype=int)  #Store the final result
        self.inc = 99999

    def match(self, u):

        # global inc
        u = int(u)
        self.ux[u] = 1  #record node that was explored
        for v in range(self.y_number):
            if self.uy[v] == 0:
                t = self.lx[u] + self.ly[v] - self.Net[u][v]
                if t == 0:   #it means here is possible to find a pair
                    self.uy[v] = 1
                    if self.result[v] == -1 or self.match(self.result[v]):
                        self.result[v] = u
                        return 1
                elif self.inc > t:
                    self.inc = t
        return 0

    def Kuh_Munkras(self):
        #initialize lx,ly
        for p in range(self.y_number):
            self.ly[p] = 0
            self.result[p] = -1
        for p in range(self.x_number):
            self.lx[p] = -999999 #minus infinite
            for q in range(self.y_number):
                if(self.lx[p] < self.Net[p][q]):    #Choose the biggest value to lx[i]
                    self.lx[p] = self.Net[p][q]

        #find the perfect match
        for u in range(self.x_number):
            while(1):
                self.inc = 999999  # the minimum gap
                self.coverUsed()
                if(self.match(u)):
                    break

                for i in range(self.x_number):  #Change sign,and try again
                    if (self.ux[i]):
                        self.lx[i] -= self.inc
                for j in range(self.y_number):
                    if (self.uy[j]):
                        self.ly[j] += self.inc
        return self.result


    def calculateSum(self):
        sum = 0
        for i in range(self.y_number):
            sum += self.Net[self.result[i]][i]
        return sum

    def getResult(self):
        return self.result

    def set_Net(self,Net):
        self.Net = Net

    def coverUsed(self):
        self.ux, self.uy = np.zeros(self.x_number, dtype=int), np.zeros(self.y_number, dtype=int)  # variable for record path

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值