最大流之二分图匹配

二分图匹配模型匈牙利算法的复杂度为O(nm)O(nm)O(nm) 最大流(Dinic) 复杂度为O(mn)O(m\sqrt{n})O(mn​)。

二分图匹配问题见图方式较为固定,设两个集合男孩集合A 和 女孩集合B 进行配对,首先从源点向女生集合(男生具体哪个集合连源点根据题目所给的边决定)中的所有点连一条边,从另外一个集合中所有点向汇点连一条边,边权均为1跑最大流即为二分图匹配数。

飞行员配对方案

根据题意可得,外籍飞行员和英国本土飞行员两两成为一组,典型的二分图匹配问题,输出的答案为二分图匹配数,直接上最大流集合。

建图大致如下:

证明:

​ (1)、流量守恒:对于每个点都有两种可能,一是成功找到相匹配的飞行员,在这对匹配中的两个点流入流出的流量均为1,满足流量守恒。二是没有找到相匹配的飞行员,流入流出的流量均为0,仍然满足流量守恒。

​ (2)、容量限制:可以看出流量只有可能是0或者1,虽然在dinic算法中的流量不一定是整型,但是流量的这两种取值均满足熔炼限制,且整型流的最大流也一定是整个流网络的最大流。

参考代码:

#include<iostream>
#include<cstring>
#include<algorithm>
#include<cstdio>
#define int long long
using namespace std;
const int N = 1e3 + 10, M = 1e4 + 10, inf = 1e18;
int n, m, S, T;
int h[N], e[M], ne[M], f[M], idx;
int q[N], d[N], cur[N];
void add(int a, int b, int c){e[idx] = b, f[idx] = c, ne[idx] = h[a], h[a] = idx ++;e[idx] = a, f[idx] = 0, ne[idx] = h[b], h[b] = idx ++;
}
bool bfs(){int hh = 0, tt = 0;memset(d, -1, sizeof(d));q[0] = S;d[S] = 0;cur[S] = h[S];while(hh <= tt){int t = q[hh ++];for(int i = h[t] ; i != -1; i = ne[i]){int ver = e[i];if(d[ver] == -1 && f[i]){cur[ver] = h[ver];d[ver] = d[t] + 1;if(ver == T){return true;}q[++ tt] = ver;}}}return false;
}
int find(int u, int limit){if(u == T){return limit;}int flow = 0;for(int i = cur[u]; i != -1 && flow < limit; i = ne[i]){cur[u] = i;int ver = e[i];if(d[ver] == d[u] + 1 && f[i]){int t = find(ver, min(f[i], limit - flow));if(!t){d[ver] = -1;}f[i] -= t;f[i ^ 1] += t;flow += t;}}return flow;
}
int dinic(){int r = 0, flow;while(bfs()){while(flow = find(S, inf)){r += flow;}}return r;
}
signed main(){cin >> m >> n;memset(h, -1, sizeof(h));S = 0;T = n + 1;// 总共n个飞行员 1 ~ m 号为外籍, m + 1 ~ n 号为英国飞行员。for(int i = 1; i <= m ; i ++){add(S, i, 1);}for(int i = m + 1; i <= n ; i ++){add(i, T, 1);}int a, b;while(cin >> a >> b){if(a == -1 && b == -1){break;}add(a, b, 1);}// 输出结果, 即统计有哪些边的流量满流并且出点是本土飞行员。printf("%lld\n", dinic());for(int i = 0; i < idx; i += 2){if(e[i] > m && e[i] <= n && !f[i]){printf("%lld %lld\n", e[i ^ 1], e[i]);}}
}

假期宿舍

根据题意可得,有一些在校生放假要回家,则他的床位可以给他认识的同学使用。也有放假不想回家的学生他可能会邀请一些非本校的学生来学校玩,问题是床位够不够这些来学校玩的外校生,和放假不回家的本校生使用,注意:一个床只能够一个人使用。也是经典的二分图匹配问题,总共n个学生,设其中需要使用床位的有x个,本校生有m个,学生和床两两配对,问存不存在x个配对使得所有需要勇床位的学生都能够分到床。

直接最大流求二分图匹配,看最后的匹配数是否等于x。

大致见图如下:

参考代码:

#include<iostream>
#include<cstring>
#include<algorithm>
#include<cstdio>
#define int long long
using namespace std;
const int N = 110, M = 2e4 + 10, inf = 1e18;
int h[N], ne[M], e[M], f[M], idx;
int q[N], d[N], cur[N];
int n, m, S, T;
int w[N];
void add(int a, int b, int c){e[idx] = b, f[idx] = c, ne[idx] = h[a], h[a] = idx ++;e[idx] = a, f[idx] = 0, ne[idx] = h[b], h[b] = idx ++;
}
bool bfs(){int hh = 0, tt = 0;memset(d, -1, sizeof(d));q[0] = S;d[S] = 0;cur[S] = h[S];while(hh <= tt){int t = q[hh ++];for(int i = h[t]; i != -1; i = ne[i]){int ver = e[i];// 类似于最短路 将图分层if(d[ver] == -1 && f[i]){d[ver] = d[t] + 1;cur[ver] = h[ver];// 已经找到汇点了if(ver == T){return true;}q[++ tt] = ver;}}}return false;
}
int find(int u, int limit){// S 到 u 的流量为limit if(u == T){return limit;}// 记录 u 向后流到T的流量int flow = 0;// 当 flow >= limit 就不用再搜索了, 因为瓶颈在前面一部分for(int i = cur[u]; i != -1 && flow < limit; i = ne[i]){// 当前弧优化cur[u] = i;int ver = e[i];// 将图分层之后找增广路径时只能从前一层到后面一层的点。if(d[ver] == d[u] + 1 && f[i]){int t = find(ver, min(limit - flow, f[i]));// 不存在增广路径时直接将该点删去if(!t){d[ver] = -1;}// 更新残留网络f[i] -= t;f[i ^ 1] += t;flow += t;}}return flow;
}
int dinic(){int r = 0, flow;while(bfs()){while(flow = find(S, inf)){r += flow;}}return r;
}
signed main(){int my_text;scanf("%lld", &my_text);while(my_text --){scanf("%lld", &n);memset(h, -1, sizeof(h));memset(w,  0, sizeof(w));idx = 0;S = 0;T = 2 * n + 1;int sum = 0;for(int i = 1; i <= n  ;i ++){scanf("%lld", &w[i]);if(w[i] == 1){add(i + n, T, 1);}}for(int i = 1; i <= n ; i ++){int x;scanf("%lld", &x);if((w[i] == 1 && x == 0) || (w[i] == 0)){add(S, i, 1);sum ++;}}for(int i = 1; i <= n ; i ++){for(int j = 1; j <= n ; j ++){int x;scanf("%lld", &x);if(x == 1 || i == j){add(i, j + n, 1);}}}if(dinic() >= sum){printf("^_^\n");}else{printf("T_T\n");}}
}

圆桌问题

由题意得,在一个可行流中第 iii个单位派出rir_iri​个代表,则有rir_iri​ 个代表会从该点流出到nnn张桌子之一的桌子,可以从源点连一条边权为1的边到该点,第iii 个桌子可以容纳cic_ici​ 个代表,可以从该点向汇点连一条容量为cic_ici​ 的边。

建图大概是这样滴:

参考代码:

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
#define int long long
using namespace std;
const int N = 1e3 + 10, M = 2e5 + 10, inf = 1e18;
int h[N], e[M], ne[M], f[M], idx;
int d[N], q[N], cur[N];
int n, m, S, T;
void add(int a, int b, int c){e[idx] = b, f[idx] = c, ne[idx] = h[a], h[a] = idx ++;e[idx] = a, f[idx] = 0, ne[idx] = h[b], h[b] = idx ++;
}
bool bfs(){int hh = 0, tt = 0;memset(d, -1, sizeof(d));q[0] = S;d[S] = 0;cur[S] = h[S];while(hh <= tt){int t = q[hh ++];for(int i = h[t]; i != -1; i = ne[i]){int ver = e[i];if(d[ver] == -1 && f[i]){d[ver] = d[t] + 1;cur[ver] = h[ver];if(ver == T){return true;}q[++ tt] = ver;}}}return false;
}
int find(int u, int limit){if(u == T){return limit;}int flow = 0;for(int i = cur[u]; i != -1 ;i = ne[i]){cur[u] = i;int ver = e[i];if(d[ver] == d[u] + 1 && f[i]){int t = find(ver, min(f[i], limit - flow));if(!t){d[ver] = -1;}f[i] -= t;f[i ^ 1] += t;flow += t; }}return flow;
}
int dinic(){int r = 0, flow;while(bfs()){while(flow = find(S, inf)){r += flow;}}return r;
}
signed main(){scanf("%lld%lld",&m, &n);memset(h, -1, sizeof(h));S = 0;T = n + m + 1;int sum = 0;for(int i = 1; i <= m; i ++){int x;scanf("%lld",&x);add(S, i, x);sum += x;}for(int i = 1; i <= n ; i ++){int x;scanf("%lld", &x);add(i + m, T, x);}for(int i = 1; i <= m ; i ++){for(int j = 1; j <= n ; j ++){add(i, j + m, 1);}}if(dinic() == sum){printf("1\n");for(int i = 1; i <= m ; i ++){for(int j = h[i]; j != -1; j = ne[j]){if(e[j] > m && e[j] <= n + m && !f[j]){printf("%lld ", e[j] - m);}}printf("\n");}}else{printf("0\n");}
}

网络流(二)最大流之二分图匹配相关推荐

  1. [Cogs14] [网络流24题#1] 飞行员分配方案 [网络流,最大流,二分图匹配]

    经典二分图匹配,可以用匈牙利算法,也可以用最大流 代码如下(Dinic): #include <iostream> #include <cstdio> #include < ...

  2. HDU 3081 Marriage Match II (并查集+二分+最大流 | 并查集+二分图匹配)

    题意:n 个男生.n个女生玩游戏,每个女生都可以和她不讨厌的男生结婚,此外她的朋友如果也不讨厌这个男生,也可以和他结婚:对于女生,如果A和B是朋友,B和C是朋友,那么A和C也是朋友.每次游戏女生会找一 ...

  3. 经典网络流题目模板(P3376 + P2756 + P3381 : 最大流 + 二分图匹配 + 最小费用最大流)...

    题目来源 P3376 [模板]网络最大流 P2756 飞行员配对方案问题 P3381 [模板]最小费用最大流 最大流 最大流问题是网络流的经典类型之一,用处广泛,个人认为网络流问题最具特点的操作就是建 ...

  4. 【网络流24题】搭配飞行员(最大流+二分图匹配)

    传送门 搭配飞行员     题意:二分图匹配裸题,不多说 I think Dinic算法跑最大流解决||匈牙利算法 Code 代码一:Dinic #include<cstdio> #inc ...

  5. 【网络流24题】解题报告:E 、圆桌问题(最大流求二分图多重匹配)

    E .圆桌问题(最大流求二分图多重匹配)[省选/NOI- ] 可以直观的想到,二分图的左边是单位,右边是桌子 由于题目的限制 每个单位只能在一个桌子坐一个人 所以我们就把每个单位向各个桌子连一道流量为 ...

  6. 洛谷P3386:网络流之二分图匹配,最大流算法

    二分图:我的理解是,对图中的点集,可分为两个集合U和V,使得两个集合之间存在通路,且集合内部不存在通路.如上图. 匹配:两两不含公共端点的边集合M 最大匹配:边数最多的匹配 完美匹配:最大匹配的匹配数 ...

  7. 紫书:二分图匹配 最大流解决

    网络流的一个经典应用是二分图匹配. 匹配是指:两两没有公共点的边集. 二分图是指:可以把结点集分成两部分X和Y,使得每条边恰好一个端点在XXX,另一个端点在YYY.换句话说,同色节点不相邻,进行二染色 ...

  8. nyoj-239 月老的难题 (二分图匹配—匈牙利算法 网络流—Dinic算法)

    月老的难题 时间限制:1000 ms  |  内存限制:65535 KB 难度:4 描述 月老准备给n个女孩与n个男孩牵红线,成就一对对美好的姻缘. 现在,由于一些原因,部分男孩与女孩可能结成幸福的一 ...

  9. 「LibreOJ Round #11」Misaka Network 与测试 (网络流跑二分图匹配)

    description 研究者们想要测试 Misaka Network,于是他们把 Misaka Network 中的所有妹妹们召集到了一起. 现在妹妹们排成了 N行 M 列,有的位置没有人.现在研究 ...

最新文章

  1. 1.IocDI和Spring
  2. 【已解决】可以访问Tomcat下webapp中的文件目录(间接实现下载功能)
  3. SpringBoot接口参数校验
  4. webpack 配置
  5. python3 unicode字符串_【已解决】Python3中如何声明字符串是unicode类型以避免log日志打印出错...
  6. cannot load php5,Cannot load php5apache2_4.dll into server解决办法
  7. 网上邻居不能正常访问的处理
  8. 如何对系统日志中事件 ID 9、事件 ID 11 和事件 ID 15 错误消息进行故障排除
  9. 4G多卡聚合路由器解决视频直播中的网络不稳定问题
  10. 关于refresh token的总结
  11. 代码规范 任重而道远
  12. VS中更改exe程序图标
  13. 道琼斯200万高危人群名单泄露,从AWS上
  14. php笔刷怎么安装方法,新手必看:Photoshop笔刷画笔工具基本使用教程
  15. 页面扫描二维码下载apk ,区分安卓,苹果
  16. 作为一名11年的大数据架构师,没有我搞不定的需求~
  17. 四神分析报告设计器 1.1.9更新
  18. 旧电脑变新电脑操作方法
  19. 基于JavaEE大学生兼职管理平台系统
  20. 查看源代码php_是否可以查看PHP源代码?

热门文章

  1. Linux下pager-taglib分页中文搜索乱码
  2. axure文本框添加水印_axure怎么打竖排字
  3. Opencv图像基本操作
  4. iPhone抓包stream
  5. sqlserver 18456
  6. linux服务器开发二(系统编程)--线程相关
  7. PR(角标)字幕模板 创意时尚品牌人物形象介绍(角标)字幕动画PR模板
  8. GitHub+Git
  9. 做好扁平化设计-视觉篇
  10. NS网络协议仿真(1)