首页 > 编程语言 >二分图匹配(匈牙利算法和KM算法)

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

时间:2023-03-14 21:04:22浏览次数:39  
标签:二分 相等 匹配 顶标 int 增广 子图 KM 算法

二分图匹配

对于一个二分图,其匹配是一个边的集合,每条边不应用重复的点

一个二分图

它有一个匹配,为图中红色线段
但这个匹配不是(边数)最大的,因此不是最大匹配

匈牙利算法

匈牙利算法用增广路径寻找最大匹配

增广路径

增光路经以一条非匹配边开始和结束,中间交替出现非匹配边和匹配边
增广路

将增广路径上的边取反,匹配边变未匹配,未匹配变匹配
发现多了一条匹配边,这是增广路的一个性质
增广路2
匈牙利算法用增广路的性质,匹配边数不断加一,直到找不到增广路\

bool dfs(int x)
{
    //返回1表示有增广路
    for(auto y : q[x])
    {
        if(!vis[y])//没有访问过
        {
            if(!cet[y]||dfs(cet[y]))
            //如果y没有与任何点匹配,或y已经匹配,但往下找有增广路
            {
                cet[y]=x;//重新连接
                return 1;
            }
        }
    }
    return 0;
}
int hungary()
{
    int ans = 0;
    for (int i = 1; i <= n; i++)
    {
        memset(vis, 0, sizeof(vis));
        if (dfs(i))//有增广路
            ans++;
    }
    return ans;
}

KM算法

匈牙利算法在面对最佳匹配时不尽人意

不尽人意

$1+1>114514?$

有没有什么好办法呢?

不尽人意2

加一条$有关紧要$的边,总之就是使图每两点都有边
此时$1+1<0+114514$,我们把$0+114514$这样的,又是最大匹配,权值又最大的匹配称为$最大权匹配$
$KM算法$求解最大权匹配,显然,如果不加有关紧要的边,它求的就是最大匹配
$KM算法$设置顶标,顶标是此点所连的最大边的权值,特别地,另一部分的顶标为0

涂

相等子图,由顶标和为其边值的两点的边组成

相等子图1

现在相等子图中找一条增广路径,显然它是$1-3$这一条边
取反,再找一次,却没有增广路径了

对于这种情况,我们需要扩大相等子图
在子图中,左部减少$d$,右部增加$d$
对于边

  • 对于两端都在相等子图中的,顶标和不变,仍位于子图中
  • 对于两端都不在相等子图中的,顶标和不变,仍不在子图中
  • 对于左端顶点在相等子图中的,顶标和减小,可能成为同志相等子图中的边
  • 对于右端顶点在相等子图中的,顶标和减小,仍不在子图中

所以,如何把$d$值控制的柔韧有余,就可以防止出现顶标和小于边权的情况

我们注意到对于左端顶点在相等子图中的,顶标和减小,可能加入相等子图
所以对于所有不完整增广路径中的边的左端点,都应该让左部的顶标减少$d$
$d$的值是多少呢,显然,只有上面所说的边的顶标和,与其边之差中的最小差,显然就是最好的$d$

定理:相等子图中,如果存在整个图的完备匹配,则其边权和为所有顶点顶标和且此匹配即为最佳匹配
原因:因为我们减的是最小的$d$,进入的边肯定就是最大的,当出现完备匹配时,所有点所连的最大边,都已经在相等子图中了,显然,这就是最佳匹配

int n, w[N][N];//边权
int cet[N], mn;//连接;求d的最小值
int vx[N], vy[N], wx[N], wy[N];//标记数组和顶标数组

bool dfs(int x)//找增广路径
{
    vx[x] = 1;//标记位于不完整增广路径内
    for (int i = 1; i <= n; i++)
    {
        if (vy[i])
            continue;
        int t = wx[x] + wy[i] - w[x][i];//可能的d
        if (!t)//t==0时,位于相等子图内
        {
            vy[i] = 1;//标记位于不完整增广路径内
            if ((!cet[i] || dfs(cet[i]))&&(cet[i]=x))
                return 1;
        }
        else
            mn = min(mn, t);//求最小的d值
    }
    return 0;
}

int km()
{
    for (int i = 1; i <= n; i++)
    {
        cet[i] = wy[i] = 0, wx[i] = -I;
        for (int j = 1; j <= n; j++)
            wx[i] = max(wx[i], w[i][j]);//左部为其所连之最大边
    }//初始化
    for (int i = 1; i <= n; i++)
    {
        for (;;)
        {
            mn = I;
            #define init(x) memset(x, 0, sizeof(x))
            init(vx), init(vy);
            if (dfs(i))//能找到增广路
                break;
            for (int j = 1; j <= n; j++)
            {
                if (vx[j])
                    wx[j] -= mn;//左部的顶标减去d
                if (vy[j])
                    wy[j] += mn;//右部的顶标加上d
            }
        }
    }
    int ans = 0;
    for (int i = 1; i <= n; i++)
        ans += w[cet[i]][i];//计算最佳匹配的答案
    return ans;
}

此外的

相等子图还有其他性质,

  1. 在任意时刻,相等子图上的最大权匹配一定小于等于相等子图的顶标和
    证:显然地,可能有的点在相等子图内但却没有在最大权匹配中,你总不能把它们忽略不计吧
  2. 在任意时刻,相等子图的顶标和即为所有顶点的顶标和
    证:仔细观察,左边的都进子图了,右边没进去的顶标都是0,不影响
  3. 扩充相等子图后,相等子图的顶标和将会减小
    证:因为,一开始是,左边的每一个点都进入相等子图,但右边却不可能不然这题就不用做了
    当我们给左边的顶标做减法时,减的值多余给右边加的值,顶标和自然会减少

非常欢迎同志们指正错误

标签:二分,相等,匹配,顶标,int,增广,子图,KM,算法
From: https://www.cnblogs.com/ssj233-aaa/p/17216332.html

相关文章

  • 多源最短路算法——Floyd算法(转载)
    1.多源最短路简介:我们知道单源最短路是指从某一个源点到图中的其它顶点的最短路。多源最短路就是指每一个点到图中其他顶点的最短路。那么有的人肯定想我知道求单源最短路......
  • Qt 算法->程序运行时间(计时函数)
    参考:Qt算法->程序运行时间(计时函数)_qtclock函数_男银的骄傲的博客-CSDN博客 用的这个博客里的方法 QT笔记(7)——Qt利用QTime计算程序运行时间_abcvincent的博客-CSDN......
  • 力扣 (LeetCode)刷题--704. 二分查找
    二分查找是一个非常基础的算法给定一个n个元素有序的(升序)整型数组nums和一个目标值target,写一个函数搜索nums中的target,如果目标值存在返回下标,否则返回-1。示......
  • 【排序算法】希尔排序
    1 前言今天把排序的几个算法过一下,这节我们看一下希尔排序,简单的来说就是多次插入排序,我们看示例。2 代码示例/***希尔排序,也就是多次插入排序*可以参考插入......
  • 800. 数组元素的目标和(双指针,二分)
    https://www.acwing.com/problem/content/802/二分:枚举a,对于每一个a[i],都二分一下求x-a[i],是否在b数组中#include<iostream>usingnamespacestd;constintN=1......
  • 【排序算法】插入排序
    1 前言今天把排序的几个算法过一下,这节我们看一下插入排序,简单的来说就是从第2个元素往前寻找位置进行插入,我们看示例。2 代码示例/***插入排序*从第2个元素......
  • 【排序算法】直接选择排序
    1 前言今天把排序的几个算法过一下,这节我们看一下直接选择排序,简单的来说就是默认某个位置为最小然后从位置后的元素逐个比较进行交换,我们看示例。2 代码示例/**......
  • 算法-练习2
    题1给你两个 非空的链表,表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储的,并且每个节点只能存储 一位 数字。请你将两个数相加,并以相同形式返回一个表......
  • 二分查找法
    二分查找法functionbinarySearch($arr,$val,$hight,$low=0){$i=0;while($low<=$hight){$i++;$mid=ceil(......
  • 笔试算法《字符串排序_1》
    题目描述编写一个程序,将输入字符串中的字符按如下规则排序。规则1:英文字母从A到Z排列,不区分大小写。如,输入:Type输出:epTy规则2:同一个英文字母的大小写同时存在时,......