算法笔记--最大流和最小割 && 最小费用最大流 && 上下界网络流
最大流:
给定指定的一个有向图,其中有两个特殊的点源S(Sources)和汇T(Sinks),每条边有指定的容量(Capacity),求满足条件的从S到T的最大流(MaxFlow)。
最小割:
割是网络中定点的一个划分,它把网络中的所有顶点划分成两个顶点集合S和T,其中源点s∈S,汇点t∈T,从S出发指向T的边的集合,称为割(S,T),这些边的容量之和称为割的容量。容量最小的割称为最小割。
根据最大流最小割定理,最大流等于最小割。
其他:
求最小割边的个数的方法:
①建边的时候每条边权 w = w * (E + 1) + 1;这样得到最大流 maxflow / (E + 1) ,最少割边数 maxflow % (E + 1)
②建图,得到最大流后,图中边若满流,说明该边是最小割上的边;再建图,原则:满流的边改为容量为 1 的边,未满流的边改为容量 INF 的边,然后最大流即答案
Ford-Fulkerson算法模板:
const int INF = 0x7f7f7f7f;
struct edge {
int to, w, rev;
};
vector<edge> g[N];
bool vis[];
void add_edge(int from, int to, int w) {
g[from].pb((edge){to, w, g[to].size()});
g[to].pb((edge){from, , g[from].size()-});
}
//通过dfs寻找增广路
int dfs(int u, int t,int f) {
if (u == t) return f;
vis[u] = true;
for (int i = ; i < g[u].size(); i++) {
edge &e = g[u][i];
if(!vis[e.to] && e.w > ) {
int d = dfs(e.to, t, min(f, e.w));
if(d > ) {
e.w -= d;
g[e.to][e.rev].w += d;
return d;
}
}
}
return ;
}
int max_flow(int s, int t) {
int flow = ;
while(true) {
mem(vis, false);
int f = dfs(s, t, INF);
if (f == ) return flow;
flow += f;
}
return flow;
}
Dinic算法+弧优化模板:
const int INF = 0x7f7f7f7f;
int level[N], iter[N];
struct edge {
int to, w, rev;
};
vector<edge>g[N];
void add_edge(int u, int v, int w) {
g[u].pb(edge{v, w, g[v].size()});
g[v].pb(edge{u, , g[u].size()-});
}
void bfs(int s) {
mem(level, -);
queue<int>q;
level[s] = ;
q.push(s);
while (!q.empty()) {
int u = q.front();
q.pop();
for (int i = ; i < g[u].size(); i++) {
edge e = g[u][i];
if(e.w > && level[e.to] < ) {
level[e.to] = level[u] + ;
q.push(e.to); }
}
}
}
int dfs(int u, int t, int f) {
if(u == t ) return f;
for (int &i = iter[u]; i < g[u].size(); i++) {
edge &e = g[u][i];
if(e.w > && level[u] < level[e.to]) {
int d = dfs(e.to, t, min(f, e.w));
if(d > ) {
e.w -= d;
g[e.to][e.rev].w +=d;
return d;
}
}
}
return ;
}
int max_flow(int s, int t) {
int flow = ;
while(true) {
bfs(s);
if(level[t] < ) return flow;
int f;
mem(iter, );
while ((f = dfs(s, t, INF)) > ) {
flow += f;
}
}
}
建图:把每头牛拆成两个,然后之间建一条容量为1的边,然后把food和drink分别放在牛的两边,分别和牛建容量为1的边,然后再在food和drink左右两边分别加一个源点s和汇点t。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<vector>
#include<algorithm>
using namespace std;
#define LL long long
#define pb push_back
#define mem(a, b) memset(a, b, sizeof(a)) const int N = ;
const int INF = 0x7f7f7f7f;
struct edge {
int to, w, rev;
};
vector<edge> g[N];
bool vis[];
void add_edge(int from, int to, int w) {
g[from].pb((edge){to, w, g[to].size()});
g[to].pb((edge){from, , g[from].size()-});
}
int dfs(int u, int t,int f) {
if (u == t) return f;
vis[u] = true;
for (int i = ; i < g[u].size(); i++) {
edge &e = g[u][i];
if(!vis[e.to] && e.w > ) {
int d = dfs(e.to, t, min(f, e.w));
if(d > ) {
e.w -= d;
g[e.to][e.rev].w += d;
return d;
}
}
}
return ;
}
int max_flow(int s, int t) {
int flow = ;
while(true) {
mem(vis, false);
int f = dfs(s, t, INF);
if (f == ) return flow;
flow += f;
}
return flow;
}
int main() {
int n, f, d, t, _t, u;
scanf("%d%d%d", &n, &f, &d);
for (int i = ; i <= n; i++) {
add_edge(i, i+n, );
}
for (int i = ; i <= n; i++) {
scanf("%d%d", &t, &_t);
while(t--) {
scanf("%d", &u);
add_edge(u+*n, i, );
}
while(_t--) {
scanf("%d", &u);
add_edge(i+n, u+*n+f, );
}
}
for (int i = ; i <= f; i++) {
add_edge(, i+*n, );
}
for (int i = ; i <= d; i++) {
add_edge(i+*n+f, , );
}
printf("%d\n", max_flow(, ));
return ;
}
建图:把每台设备拆成两个,然后之间建一条容量为设备操作台数的边,然后在所有的input里找没有1的设备,与源点s相连,在所有的output里找没有0的设备,与汇点t相连。然后设备之间如果input和output能匹配,就相连。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<vector>
#include<queue>
#include<algorithm>
using namespace std;
#define LL long long
#define pb push_back
#define mem(a, b) memset(a, b, sizeof(a)) const int N = ;
const int INF = 0x7f7f7f7f;
int level[N*], iter[N*], in[N][], out[N][];
bool vis[N][N];
struct edge {
int to, w, rev;
};
struct node {
int x, y, z;
}res[N*N];
vector<edge>g[N*];
void add_edge(int u, int v, int w) {
g[u].pb(edge{v, w, g[v].size()});
g[v].pb(edge{u, , g[u].size()-});
}
void bfs(int s) {
mem(level, -);
queue<int>q;
level[s] = ;
q.push(s);
while (!q.empty()) {
int u = q.front();
q.pop();
for (int i = ; i < g[u].size(); i++) {
edge e = g[u][i];
if(e.w > && level[e.to] < ) {
level[e.to] = level[u] + ;
q.push(e.to); }
}
}
}
int dfs(int u, int t, int f) {
if(u == t ) return f;
for (int &i = iter[u]; i < g[u].size(); i++) {
edge &e = g[u][i];
if(e.w > && level[u] < level[e.to]) {
int d = dfs(e.to, t, min(f, e.w));
if(d > ) {
e.w -= d;
g[e.to][e.rev].w +=d;
return d;
}
}
}
return ;
}
int max_flow(int s, int t) {
int flow = ;
while(true) {
bfs(s);
if(level[t] < ) return flow;
int f;
mem(iter, );
while ((f = dfs(s, t, INF)) > ) {
flow += f;
}
}
}
int main() {
int p, n, w;
while(~ scanf("%d%d", &p, &n)) {
for (int i = ; i < N*; i++) g[i].clear();
mem(vis, false);
for (int i = ; i <= n; i++) {
scanf("%d", &w);
add_edge(i, i+n, w);
for (int j = ; j <= p; j++) scanf("%d", &in[i][j]);
for (int j = ; j <= p; j++) scanf("%d", &out[i][j]);
}
for (int i = ; i <= n; i++) {
bool f = true, _f = true;
for (int j = ; j <= p; j++) if(in[i][j] == ) f = false;
for (int j = ; j <= p; j++) if(!out[i][j]) _f = false;
if(f) add_edge(, i, INF);
if(_f) add_edge(i+n, , INF);
for (int j = i + ; j <= n; j++) {
bool f = true, _f = true;
for (int k = ; k <= p; k++) {
if(in[j][k] == );
else {
if(out[i][k] != in[j][k]) f = false;
}
if(in[i][k] == );
else {
if(out[j][k] != in[i][k]) _f = false;
}
}
if(f) add_edge(i+n, j, INF), vis[i][j] = true;
if(_f) add_edge(j+n, i, INF), vis[j][i] = true;
}
}
int ans = max_flow(, );
int cnt = ;
for (int i = ; i <= n; i++) {
if(g[i].size() == )continue;
for (int j = ; j < g[i].size(); j++) {
if(g[i][j].to == || g[i][j].to == || g[i][j].to == i+n || g[i][j].to <= n ) continue;
if( < g[i][j].w && vis[g[i][j].to-n][i]) {
res[++cnt].x = g[i][j].to - n;
res[cnt].y = i;
res[cnt].z = g[i][j].w;
}
}
}
printf("%d %d\n", ans, cnt);
for (int i = ; i <= cnt; i++) printf("%d %d %d\n", res[i].x, res[i].y, res[i].z);
}
return ;
}
最小费用最大流:
坑
详见挑战程序设计
上下界网络流:
https://www.cnblogs.com/NineSwords/p/9415962.html
算法笔记--最大流和最小割 && 最小费用最大流 && 上下界网络流的更多相关文章
- 【BZOJ-2229】最小割 最小割树(最大流+分治)
2229: [Zjoi2011]最小割 Time Limit: 10 Sec Memory Limit: 259 MBSubmit: 1565 Solved: 560[Submit][Status ...
- [BZOJ2502]清理雪道 有上下界网络流(最小流)
2502: 清理雪道 Time Limit: 10 Sec Memory Limit: 128 MB Description 滑雪场坐落在FJ省西北部的若干座山上. 从空中鸟瞰,滑雪场 ...
- hdu 4940 Destroy Transportation system( 无源汇上下界网络流的可行流推断 )
题意:有n个点和m条有向边构成的网络.每条边有两个花费: d:毁坏这条边的花费 b:重建一条双向边的花费 寻找这样两个点集,使得点集s到点集t满足 毁坏全部S到T的路径的费用和 > 毁坏全部T到 ...
- CQOI2016 不同的最小割 (最小割树模板)(等价流树的Gusfield构造算法)
题目 最小割树模板 算法详解及证明见: 2016年国家队候选队员论文 <浅谈无向图最小割问题的一些算法及应用--绍兴一中 王文涛> 3.2节 CODE #include <bits/ ...
- ARC085E(最小割规划【最大流】,Dinic当前弧优化)
#include<bits/stdc++.h>using namespace std;typedef long long ll;const ll inf=0x3f3f3f3f;int cn ...
- P1345 [USACO5.4]奶牛的电信Telecowmunication【最小割】【最大流】
题目描述 农夫约翰的奶牛们喜欢通过电邮保持联系,于是她们建立了一个奶牛电脑网络,以便互相交流.这些机器用如下的方式发送电邮:如果存在一个由c台电脑组成的序列a1,a2,...,a(c),且a1与a2相 ...
- [ZJOI2011][bzoj2229] 最小割 [最小割树]
题面 传送门 思路 首先我们明确一点:这道题不是让你把$n^2$个最小割跑一遍[废话] 但是最小割过程是必要的,因为最小割并没有别的效率更高的算法(Stoer-Wagner之类的?) 那我们就要尽量找 ...
- [CQOI2016][bzoj4519] 不同的最小割 [最小割树]
题面 传送门 思路 首先我们明确一点:这道题不是让你把$n^2$个最小割跑一遍[废话] 但是最小割过程是必要的,因为最小割并没有别的效率更高的算法(Stoer-Wagner之类的?) 那我们就要尽量找 ...
- ZJOI 最小割 CQOI 不同的最小割 (最小割分治)
题目1 ZJOI 最小割 题目大意: 求一个无向带权图两点间的最小割,询问小于等于c的点对有多少. 算法讨论: 最小割 分治 代码: #include <cstdlib> #include ...
随机推荐
- centos6二进制安装mysql5.5
centos 6.5,安装mysql 5.5.60 所需安装包mysql-5.5.60-linux-glibc2.12-x86_64.tar.gz.ncurses-devel-5.7-4.200902 ...
- Centos7部署tornado项目
今天帮一个学生解决tornado的部署问题,在此记录了这其中的过程,其中的tornado项目更换为demo示例. 开发环境: 本地开发环境:Win10 + Python3.5.4 + PyCharm ...
- 【python004-分支循环】
一.条件分支 1.第一个改进要求:游戏猜错的时候程序提示用户当前的输入比答案大了还是小了 python的比较操作符: > 左边大于右边 >= 左边大于等于右边 ...
- 前端 --- 1 HTML
一.文档结构 <!DOCTYPE html> <html lang="zh-CN"> #这个lang表示语言,zh-CN是中文的意思, 如果以英文为主,就写 ...
- linux内核中的fuse是什么?
答: 一个用户态文件系统框架,属于内核的一种特性. 1.组成部分 fuse.ko(内核模块) + libfuse.*(用户空间库) + fusemount(挂载工具) 2.参考资料 fuse.txt
- POJ 2400 Supervisor, Supervisee(KM二分图最大权值匹配)题解
题意:n个老板n个员工,先给你n*n的数据,i行j列代表第i个老板第j喜欢的员工是谁,再给你n*n的数据,i行j列代表第i个员工第j喜欢的老板是谁,如果匹配到第k喜欢的人就会产生一个分数k-1.现在让 ...
- android 控件获取 获取焦点
控件.setEnabled(true);控件.setFocusable(true);控件.setFocusableInTouchMode(true);控件.requestFocus();控件.requ ...
- 简单数论总结1——gcd与lcm
并不重要的前言 最近学习了一些数论知识,但是自己都不懂自己到底学了些什么qwq,在这里把知识一并总结起来. 也不是很难的gcd和lcm 显而易见的结论: 为什么呢? 根据唯一分解定理: a和b都可被分 ...
- 4-Four-Seeing hands
①Several cases have been reported in Russia recently of people who can read and detect colours wit ...
- LeetCode - 198 简单动态规划 打家劫舍
你是一个专业的小偷,计划偷窃沿街的房屋.每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警. 给定一个代表每 ...