【算法笔记】二分图最大权匹配 - KM算法(dfs版O(n4) + bfs版O(n3))
整理的算法模板合集: ACM模板
匈牙利算法又称为 KM 算法,可以在 O(n3)O(n^3)O(n3) 时间内求出二分图的 最大权完美匹配 。
考虑到二分图中两个集合中的点并不总是相同,为了能应用 KM 算法解决二分图的最大权匹配,需要先作如下处理:将两个集合中点数比较少的补点,使得两边点数相同,再将不存在的边权重设为 0,这种情况下,问题就转换成求最大权完美匹配问题 ,从而能应用 KM\tt KMKM 算法求解。
其实就是普通的匈牙利算法求二分图最大匹配的拓展版本
顶标:两边点都有的标记(左 aia_iai右 bjb_jbj)满足 ai+bj≥wi,ja_i+b_j\ge w_{i,j}ai+bj≥wi,j,不唯一。
相等边:ai+bj=wi,ja_i+b_j=w_{i,j}ai+bj=wi,j的边 (i,j)(i,j)(i,j)。
相等子图:相等边构成的子图。
交错树:增广路径形成的树。
KM\tt KMKM 算法核心定理 : 对于某组可行顶标,如果其相等子图存在完美匹配,那么,该匹配就是原二分图的最大权完美匹配。
具体流程:
(1) 分配可行顶标,并对每个节点执行 (2),(3),(4)。
(2) 匈牙利算法找增广。
(3) 找不到增广路(相等子图匹配)就调整顶标。
(4) 重复 (2),(3) 直到找到增广路。
模板题:P6577 【模板】二分图最大权完美匹配
O(n4)O(n^4)O(n4)的DFS\tt DFSDFS版本
/*有些小问题,别人的dfs都能拿50分我的只能拿10分...*/
typedef long long ll;
typedef unsigned long long ull;//卡精度
typedef pair<int,int> PII;const int N = 507, M = 5e3 +7, maxn = 1007;
const int mod = 1e9+7;
const ll INF = 1e15+7;ll w[N][N];//边权
ll la[N], lb[N];//左、右部点的顶标
bool va[N], vb[N];//访问标记,是否在交错树中
int match[N];//右部点匹配的左部点(一个只能匹配一个嘛)
int n;
ll delta, upd[N];bool dfs(int x)
{va[x] = 1;//标记访问:x在交错树中for(int y = 1; y <= n; ++ y){if(!vb[y]){//从左部点找右部点(如果不在交错树上)if(la[x] + lb[y] - w[x][y] == 0){//相等子图vb[y] = 1;//标记访问:y在交错树中if(!match[y] || dfs(match[y])){//匈牙利行为match[y] = x;return true;}}}else upd[y] = min(upd[y], la[x] + lb[y] - w[x][y]);//Δ}return false;
}ll KM()
{//左右部点各n个fill(la+1,la+n+1,-INF);//fill(lb+1,lb+n+1,0);for(int i = 1;i <= n; ++ i){for(int j = 1; j <= n; ++ j)//先分配左部点la[i] = max(la[i], w[i][j]);//取它能接触到的最大值以保证满足ai+bj>=wij}for(int i = 1; i <= n; ++ i){while(true){//直到该左部点找到匹配//memset(va, 0, sizeof va);//memset(vb, 0, sizeof vb);fill(va+1,va+n+1,0);fill(vb+1,vb+n+1,0);//for(int j = 1; j <= n; ++ j)// upd[j] = INF;fill(upd+1,upd+n+1,INF);if(dfs(i)) break;//找到增广路了delta = INF;for(int j = 1; j <= n; ++ j)if(!vb[j])delta = min(delta, upd[j]);//找最小的Δfor(int j = 1; j <= n; ++ j){//修改顶标//把所有在交错树上的左右部点都左加右减 Δif(va[j])la[j] -= delta;if(vb[j])lb[j] += delta;}}}ll ans = 0;for(int i = 1; i <= n; ++ i)ans += w[match[i]][i];//答案加上所有完备匹配边的权值//注意二分图匹配是从左部点到右部点有权值//match数组存的是所有右部点匹配的左部点的编号//所以我们ans要加上w[match[i]][i]从左往右return ans;
}
int m;
int main()
{//memset(w, 0xcf, sizeof w);scanf("%d%d", &n, &m);for(int i = 1; i <= n; ++ i)for(int j = 1; j <= n; ++ j)w[i][j] = -INF;for(int i = 1 ;i <= m; ++ i){int x, y;ll z;scanf("%d%d%lld", &x, &y, &z);w[x][y] = max(w[x][y], z);//最大匹配嘛}printf("%lld\n", KM());for(int i = 1; i <= n; ++ i)printf("%d ", match[i]);puts("");return 0;
}
O(n3)O(n^3)O(n3)的BFS\tt BFSBFS迭代版本
把 DFS\tt DFSDFS 换成 BFS\tt BFSBFS。本质上没有什么区别
但是每个左边的点只会进队、搜索一次。
用p 数组记录的是增广交错树。
迭代版本的BFS\tt BFSBFS ,不需要 queue\tt queuequeue。
/*好难懂啊,还是抄板子吧*/
typedef long long ll;
typedef unsigned long long ull;//卡精度
typedef pair<int,int> PII;const int N = 507, M = 5e3 +7, maxn = 1007;
const int mod = 1e9+7;
const ll INF = 1e15+7;ll w[N][N];//边权
ll la[N], lb[N];//左、右部点的顶标
bool va[N], vb[N];//访问标记,是否在交错树中
int match[N];//右部点匹配的左部点(一个只能匹配一个嘛)
int n;
ll delta, upd[N];
int p[N];
ll c[N];void bfs(int x)
{int a, y = 0, y1 = 0;for(int i = 1; i <= n; ++ i)p[i] = 0, c[i] = INF;match[y] = x;do{a = match[y], delta = INF, vb[y] = true;for(int b = 1; b <= n; ++ b){if(!vb[b]){if(c[b] > la[a] + lb[b] - w[a][b])c[b] = la[a] + lb[b] - w[a][b], p[b] = y;if(c[b] < delta)//Δ还是取最小的delta = c[b], y1 = b;}}for(int b = 0; b <= n; ++ b)if(vb[b])la[match[b]] -= delta, lb[b] += delta;else c[b] -= delta;y = y1;}while(match[y]);while(y)match[y] = match[p[y]], y = p[y];
}ll KM()
{for(int i = 1; i <= n; ++ i)match[i] = la[i] = lb[i] = 0;for(int i = 1; i <= n; ++ i){for(int j = 1; j <= n; ++ j)vb[j] = false;bfs(i);}ll res = 0;for(int y = 1; y <= n; ++ y)res += w[match[y]][y];return res;
}int m;
int main()
{scanf("%d%d", &n, &m);for(int i = 1; i <= n; ++ i)for(int j = 1; j <= n; ++ j)w[i][j] = -INF;for(int i = 1 ;i <= m; ++ i){int x, y;ll z;scanf("%d%d%lld", &x, &y, &z);w[x][y] = max(w[x][y], z);//最大匹配嘛}printf("%lld\n", KM());for(int i = 1; i <= n; ++ i)printf("%d ", match[i]);puts("");return 0;
}
【算法笔记】二分图最大权匹配 - KM算法(dfs版O(n4) + bfs版O(n3))相关推荐
- 二分图最大权匹配 KM算法
KM算法的正确性基于以下定理: 若由二分图中所有满足A[i]+B[i]=w[i][j]的边C(i,j)构成的子图(即相等子图)有完备匹配,那么这个完备匹配就是二分图的最大权匹配 基本概念 1.完备匹配 ...
- 二分图最佳完美匹配——KM算法总结
KM 算法 求解二分图最佳完美匹配的算法. 先来看一道例题Hdu 2255. 显然是KM的裸题.假设我们要匹配集合X和Y的点,先给每个点一个顶标Lx和Ly. 为什么要给顶标? 首先顶标是我们限制边的一 ...
- 二分图的完全匹配---KM算法
写在之前:更多二分图知识,请关注--->二分图知识导航篇 引述 KM算法全称Kuhn-Munkres,是一种求二分图完全.完美.最佳等匹配的方法,其实三个形容词都是指两个集内的所有顶点能够一一匹 ...
- 二分图最佳完美匹配——KM算法
前情概要 学km算法之前,笔者还是希望大家已经掌握了匈牙利算法--也就是对于求解二分图最大匹配的算法.学习本算法的前提除了已经掌握C++语言之外,还需要掌握邻接表存图法,不会的朋友这里有传送门 [微笑 ...
- HDU - 2255 奔小康赚大钱(二分图最大权匹配+KM)
题目链接:点击查看 题目大意:中文题目,简单来说就是n个人和n个房子最大匹配,需要一一对应并且满足权值和最大 题目分析:二分图的完备匹配,条件完全符合KM算法的局限性,直接套模板即可,后续学习费用流( ...
- HDU 1853 HDU 3488【有向环最小权值覆盖问题 】带权二分图匹配 KM算法
HDU 1853 & HDU 3488[有向环最小权值覆盖问题 ]最小费用最大流 In the kingdom of Henryy, there are N (2 <= N <= ...
- 奔小康赚大钱 HDU - 2255( 二分图匹配KM算法详解)
题目 传说在遥远的地方有一个非常富裕的村落,有一天,村长决定进行制度改革:重新分配房子. 这可是一件大事,关系到人民的住房问题啊.村里共有n间房间,刚好有n家老百姓,考虑到每家都要有房住(如果有老百 ...
- 训练指南 UVALive - 4043(二分图匹配 + KM算法)
layout: post title: 训练指南 UVALive - 4043(二分图匹配 + KM算法) author: "luowentaoaa" catalog: true ...
- 【HDU 2255】奔小康赚大钱 (最佳二分匹配KM算法)
奔小康赚大钱 Time Limit: 1000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Total Sub ...
最新文章
- 内核终端判断,微信?QQ?ipad?IE?移动?Google?opera……
- DHTML【11】--DOM
- Oracle数据库中dual是什么东西啊?
- Java权限管理系统源代码下载
- 苹果iOS设备解锁软件:iToolab UnlockGo
- JavaWeb框架三剑客前言
- 图像质量评价(IQA)综述
- 技嘉ide模式怎么改_bios如何设置为ide兼容模式_怎么进入bios把硬盘模式改成ide
- 峰值速率、系统容量、吞吐量、带宽之间的区别
- 对于目标检测中mAP@0.5的理解
- 企业抖音蓝V怎么认证?申请流程是怎样的?需要具备哪些条件?
- analy32.xll下载_Android Studio 4.0添加了Motion Editor和Build Analyzer
- 2018年高德地图POI全国数据下载
- 【公务员考试】结构化面试时间一般多长?
- x86、i386、IA-32、amd64、x64、x86-64、IA-64的区别
- 22考研清华905机械设计基础上岸经验分享
- 打工人必学的法律知识(三)——《中华人民共和国劳动争议调解仲裁法》
- CSS3系列视频教程,从入门到精通
- 动画指导在Excel中批量打印标签/送货单等等
- 视频、音频格式转换(以F4V转MP4为例)