\(\mathcal{Description}\)

  Link.

  给定二分图 \(G=(V=X\cup Y,E)\),\(|X|=|Y|=n\),边 \((u,v)\in E\) 有权 \(w(u,v)\),且保证存在完美匹配。求 \(G\) 的一个匹配 \(M\),最大化 \(\sum_{(u,v)\in M}w(u,v)\)。

  \(n\le500\)。

\(\mathcal{Solution}\)

  首先我会费用流。

  Kuhn-Munkres 算法,能够在 \(\mathcal O(n^3)\) 的优秀复杂度内解决这一问题,即二分图最大权完美匹配问题。

  定义 1(可行顶标) 对于 \(u\in V\),分配一个实数顶标 \(l(u)\),若满足 \(\forall (u,v)\in E,w(u,v)\le l(u)+l(v)\),则称 \(l\) 为可行顶标。

  定义 2(相等子图) 在确定的可行顶标 \(l\) 下,定义 \(G\) 的相等子图 \(H=(V,E')\),其中 \(E'=\{(u,v)\in E\mid w(u,v)=l(u)+l(v)\}\)。

  定理 1 若相等子图 \(H\) 有完美匹配 \(M\),则 \(M\) 是 \(G\) 的最大权完美匹配。

  定理的证明是平凡的,不过值得一提的是,\(\sum l(u)\) 与 \(\sum w(u,v)x(u,v)\)(\(x(u,v)\in\{0,1\}\),表示这条边选或不选)似乎构成对偶线性规划的关系,期待读者能在此有一些思考(咕。

  接下来就落实到算法层面,我们的目标就是找到存在完美匹配的可行顶标。

  首先,任取一组可行顶标。例如,\(l(u)=[u\in X]\max_{(u,v)\in E}w(u,v)\)。

  此后,选一个未匹配点,走交错路径增广。若存在增广路就直接增广;否则,我们会通过遍历得到一棵交错路径树。令 \(S,T\) 分别表示 \(X,Y\) 中在树上的点,\(S',T'\) 则表示不在树上的点。分析集合到集合内边的性质:

  • $\forall u\in S,v\in T',(u,v)\in E,~w(u,v)<l(u)+l(v) $。

  • \(\forall u\in S',v\in T,(u,v)\in E\),\((u,v)\) 不是匹配边。

  考虑这样一个对 \(l\) 的调整,取某个正整数 \(d\),令

\[l'(u)=\begin{cases}
l(u) & u\in S'\cup T'\\
l(u)-d & u\in S\\
l(u)+d & u\in T
\end{cases}.
\]

考查新的 \(l'\) 带来的影响:

  • \((u,v)\in S\times T\) 内的边,\(l(u)+l(v)\) 不变,不影响。

  • \((u,v)\in S'\times T'\) 内的边,也不影响。

  • \((u,v)\in S\times T'\),顶标的限制从 \(l(u)+l(v)\) 变为 \(l(u)+l(v)-d\),则 \((u,v)\) 有可能加入 \(H\)。

  • \((u,v)\in S'\times T\),顶标的限制从 \(l(u)+l(v)\) 变为 \(l(u)+l(v)+d\),则 \((u,v)\) 不可能加入 \(H\)。

  可见,仅有 \(S\times T'\) 内的边影响 \(l'\) 的可行性。我们取 \(d=\min_{(u,v)\in E\cap (S\times T')}\{l(u)-l(v)-w(u,v)\}\),则必然至少一条边进入 \(H\),就能借此扩展交错路径树了。对于 \(u\in Y\),通过维护 \(s(u)=\min\{l(u)-l(v)-w(u,v)\}\),就能快速求出 \(d\)。

  分析复杂度,\(n\) 次枚举起始点,每次做至多 \(n\) 次对 \(l'\) 的更新,每次更新 \(\mathcal O(n)\),最终可以做到 \(\mathcal O(n^3)\)。

\(\mathcal{Code}\)

  注意几个细节:

  • \(l(u),s(u)\) 的规模可能很大,上界为 \(nw\),\(w\) 为边权,上界是能达到的。

  • 增广时必须从新连入交替路径树的点出发以保证复杂度。

  1. /*+Rainybunny+*/
  2. #include <bits/stdc++.h>
  3. #define rep(i, l, r) for (int i = l, rep##i = r; i <= rep##i; ++i)
  4. #define per(i, r, l) for (int i = r, per##i = r; i >= per##i; --i)
  5. typedef long long LL;
  6. template <typename Tp>
  7. inline Tp imin(const Tp& u, const Tp& v) { return u < v ? u : v; }
  8. template <typename Tp>
  9. inline Tp imax(const Tp& u, const Tp& v) { return u < v ? v : u; }
  10. const int MAXN = 500;
  11. const LL LINF = 0x3f3f3f3f3f3f3f3f;
  12. int n, m, fa[MAXN * 2 + 5], mtc[MAXN * 2 + 5];
  13. LL slk[MAXN * 2 + 5], lab[MAXN * 2 + 5], adj[MAXN + 5][MAXN + 5];
  14. bool vis[MAXN * 2 + 5];
  15. inline int augment(const int u, std::vector<int>& newS) {
  16. assert(!vis[u]), vis[u] = true;
  17. if (u > n && !mtc[u]) return u;
  18. else if (u > n) {
  19. if (int t; fa[mtc[u]] = u, t = augment(mtc[u], newS)) return t;
  20. } else {
  21. newS.push_back(u);
  22. rep (v, n + 1, n << 1) {
  23. if (!vis[v] && adj[u][v - n] == lab[u] + lab[v]) {
  24. if (int t; fa[v] = u, t = augment(v, newS)) {
  25. return t;
  26. }
  27. }
  28. }
  29. }
  30. return 0;
  31. }
  32. inline LL kuhnMunkres() {
  33. LL ret = 0;
  34. rep (u, 1, n) rep (v, 1, n) lab[u] = imax(lab[u], 0ll + adj[u][v]);
  35. rep (u, 1, n) if (!mtc[u]) {
  36. int fin, cur = u;
  37. static std::vector<int> newS; newS.clear();
  38. memset(fa, 0, sizeof fa);
  39. memset(vis, false, sizeof vis);
  40. memset(slk, 0x3f, sizeof slk);
  41. while (!(fin = augment(cur, newS))) {
  42. LL d = LINF;
  43. rep (v, n + 1, n << 1) if (!vis[v]) {
  44. for (int u: newS) {
  45. if (LL t = lab[u] + lab[v] - adj[u][v - n]; t < slk[v]) {
  46. slk[v] = t, fa[v] = u;
  47. }
  48. }
  49. d = imin(d, slk[v]);
  50. }
  51. newS.clear(), cur = 0;
  52. rep (u, 1, n) if (vis[u]) lab[u] -= d;
  53. rep (v, n + 1, n << 1) {
  54. if (vis[v]) lab[v] += d;
  55. else if (!(slk[v] -= d)) cur = v;
  56. }
  57. }
  58. if (fin) {
  59. for (int v = fin, u = fa[v]; u; u = fa[v = fa[u]]) {
  60. mtc[u] = v, mtc[v] = u;
  61. ret += adj[u][v - n];
  62. if (fa[u]) ret -= adj[u][fa[u] - n];
  63. }
  64. }
  65. }
  66. return ret;
  67. }
  68. int main() {
  69. scanf("%d %d", &n, &m);
  70. rep (i, 1, n) rep (j, 1, n) adj[i][j] = -LINF;
  71. rep (i, 1, m) {
  72. int u, v, w; scanf("%d %d %d", &u, &v, &w);
  73. adj[u][v] = w;
  74. }
  75. printf("%lld\n", kuhnMunkres());
  76. rep (v, n + 1, n << 1) printf("%d%c", mtc[v], v < repv ? ' ' : '\n');
  77. return 0;
  78. }

Solution -「洛谷 P6577」「模板」二分图最大权完美匹配的更多相关文章

  1. 【模板】二分图最大权完美匹配(KM算法)/洛谷P6577

    题目链接 https://www.luogu.com.cn/problem/P6577 题目大意 给定一个二分图,其左右点的个数各为 \(n\),带权边数为 \(m\),保证存在完美匹配. 求一种完美 ...

  2. 【模板】二分图最大权完美匹配KM算法

    hdu2255模板题 KM是什么意思,详见百度百科. 总之知道它可以求二分图最大权完美匹配就可以了,时间复杂度为O(n^3). 给张图. 二分图有了边权,求最大匹配下的最大权值. 所以该怎么做呢?对啊 ...

  3. 二分图最大权值匹配 KM算法 模板

    KM算法详解+模板 大佬讲的太好了!!!太好了!!! 转载自:http://www.cnblogs.com/wenruo/p/5264235.html KM算法用来求二分图最大权完美匹配. 本文配合该 ...

  4. 「区间DP」「洛谷P1043」数字游戏

    「洛谷P1043」数字游戏 日后再写 代码 /*#!/bin/sh dir=$GEDIT_CURRENT_DOCUMENT_DIR name=$GEDIT_CURRENT_DOCUMENT_NAME ...

  5. 「 洛谷 」P2768 珍珠项链

    珍珠项链 题目限制 内存限制:125.00MB 时间限制:1.00s 标准输入输出 题目知识点 动态规划 \(dp\) 矩阵 矩阵乘法 矩阵加速 矩阵快速幂 题目来源 「 洛谷 」P2768 珍珠项链 ...

  6. 「 洛谷 」P4539 [SCOI2006]zh_tree

    小兔的话 推荐 小兔的CSDN [SCOI2006]zh_tree 题目限制 内存限制:250.00MB 时间限制:1.00s 标准输入输出 题目知识点 思维 动态规划 \(dp\) 区间\(dp\) ...

  7. 「 洛谷 」P2151 [SDOI2009]HH去散步

    小兔的话 欢迎大家在评论区留言哦~ HH去散步 题目限制 内存限制:125.00MB 时间限制:1.00s 标准输入 标准输出 题目知识点 动态规划 \(dp\) 矩阵 矩阵乘法 矩阵加速 矩阵快速幂 ...

  8. 【洛谷P3369】【模板】普通平衡树题解

    [洛谷P3369][模板]普通平衡树题解 题目链接 题意: 您需要写一种数据结构(可参考题目标题),来维护一些数,其中需要提供以下操作:1. 插入x数2. 删除x数(若有多个相同的数,因只删除一个)3 ...

  9. Solution -「CTS 2019」「洛谷 P5404」氪金手游

    \(\mathcal{Description}\)   Link.   有 \(n\) 张卡牌,第 \(i\) 张的权值 \(w_i\in\{1,2,3\}\),且取值为 \(k\) 的概率正比于 \ ...

随机推荐

  1. Cannot uninstall 'pyparsing'. It is a distutils installed project

    我的环境: [root@ansible ~]# python -V Python 2.7.5 [root@ansible ~]# cat /etc/redhat-release CentOS Linu ...

  2. 阿里云服务器ECS Ubuntu16.04 + Seafile 搭建私人网盘 (Seafile Pro)

    原文链接:? 传送门 本文主要讲述 使用 Ubuntu 16.04 云服务器 通过脚本实现对 Seafile Pro 的安装,完成私人网盘的搭建 首先给出 Seafile 专业版的下载地址(Linux ...

  3. Springboot集成邮箱服务发送邮件

    一.前言 Spring Email 抽象的核心是 MailSender 接口,MailSender 的实现能够把 Email 发送给邮件服务器,由邮件服务器实现邮件发送的功能. Spring 自带了一 ...

  4. access注入 - 联合查询

    1.access数据库简介 简介:Microsoft Office Access是由微软发布的关系数据库管理系统.它结合了 MicrosoftJet Database Engine 和 图形用户界面两 ...

  5. 数组内sizeof与strlen的区别

    1.数组在内存中是连续存放的,地址呈4个字节递增 2.数组的定义需要初始化,否则输出会已随机值输出 3.strlen()和sizeof()之间无关联:strlen():是求字符串长度的----只能针对 ...

  6. linux系统Kibana安装 汉化

    Elasticsearch官方系列软件Kibana,在控制台管理维护Elasticsearch. 这里注意Elasticsearch和Kibana的版本一定要一致. 官网下载地址 https://ww ...

  7. LATEX图片位置

    常用选项[htbp]是浮动格式: -『h』当前位置.将图形放置在正文文本中给出该图形环境的地方.如果本页所剩的页面不够,这一参数将不起作用. -『t』顶部.将图形放置在页面的顶部. -『b』底部.将图 ...

  8. azure flask 测试

    本机 flask on linux service 完美.选择部署槽 web app service

  9. 集合框架-TreeSet-Comparator比较器练习(字符串长度排序)

    1 package cn.itcast.p5.treeset.test; 2 3 import java.util.Iterator; 4 import java.util.TreeSet; 5 6 ...

  10. 【转载】Systemd 入门教程:实战篇

    作者: 阮一峰 日期: 2016年3月 8日 上一篇文章,我介绍了 Systemd 的主要命令,今天介绍如何使用它完成一些基本的任务. 一.开机启动 对于那些支持 Systemd 的软件,安装的时候, ...