P3376 【模板】网络最大流( Edmonds-krap、Dinic、ISAP 算法)

题目描述 如题,给出一个网络图,以及其源点和汇点,求出其网络最大流。

输入格式 第一行包含四个正整数N、M、S、T,分别表示点的个数、有向边的个数、源点序号、汇点序号。

接下来M行每行包含三个正整数ui、vi、wi,表示第i条有向边从ui出发,到达vi,边权为wi(即该边最大流量为wi)

输出格式 一行,包含一个正整数,即为该网络的最大流。

输入输出样例

输入 #1 复制
4 5 4 3
4 2 30
4 3 20
2 3 20
2 1 30
1 3 40
输出 #1 复制
50

说明/提示

时空限制:1000ms,128M

数据规模:

对于30%的数据:N<=10,M<=25

对于70%的数据:N<=200,M<=1000

对于100%的数据:N<=10000,M<=100000

样例说明:

题目中存在3条路径:

4–>2–>3,该路线可通过20的流量

4–>3,可通过20的流量

4–>2–>1–>3,可通过10的流量(边4–>2之前已经耗费了20的流量)

故流量总计20+20+10=50。输出50。

思路

最大流板子。。。。

大佬博客传送门1

传送门2

传送门3

传说门4

题解一(Edmonds-krap)

#include<iostream>
#include<cstring>
#include<queue>
using namespace std; #define INF 0x3f3f3f3f
const int maxn = 10005;
const int maxm = 300005;
int n,m,s,e; struct Edge
{
int u,v,next;
int cap,flow;
} edge[2*maxm];
int head[2*maxm]; int k = -1;
void Add(int u, int v, int w)
{
edge[++ k] = (Edge){ u, v, head[u], w, 0};
head[u] = k;
edge[++ k] = (Edge){ v, u, head[v], 0, 0};
head[v] = k;
}
int flag = 0; //flag == 0 表示还能找到增广路 int bfs(int s, int e)
{
int min_flow[maxn]; //min_flow[i] 表示到达第 i 号节点时,之前所有的路径中 最小的 边的流量cap - 已经流过的流量flow
int pre[maxm]; //记录增光路时,当前节点是由那个那条边找到的,这样在找到增光路之后,我们可以根据 pre[] 数组回推其他 组成该增光路的边
memset(min_flow, 0, sizeof(min_flow)); //min_flow[i] == 0 表示之前没有走过 i 节点
min_flow[s] = INF;
pre[s] = -1;
queue<int> q;
q.push(s);
int u,v,flow;
while(! q.empty())
{
u = q.front(); q.pop(); for(int i = head[u]; i != -1; i = edge[i].next)
{
v = edge[i].v;
flow = edge[i].cap - edge[i].flow;
if(flow > 0 && ! min_flow[v]) //所走的这条边还有 剩余的空间cap,并且该节点还没有被走过。。
{
pre[v] = i;
min_flow[v] = min(min_flow[u], flow);
q.push(v);
}
}
if(min_flow[e])
break;
}
if(! min_flow[e]) //一只到更新结束,终点到最小增加值还是等于0,那么说从起点到终点已经没有增广路了
flag = 1;
//这一点千万不要忘,,,,把用过的水流在这条增光路上都减去。。。。
for(int j = e; pre[j] != -1; j = edge[pre[j]].u)
{
edge[pre[j]].flow += min_flow[e];
edge[pre[e]^1].flow -= min_flow[e];
}
return min_flow[e]; //返回最小增加值到 答案中
} int max_flow()
{
int mx_flw = 0;
while(1)
{
mx_flw += bfs(s, e);
if(flag)
break;
}
return mx_flw;
} void init()
{
memset(head, -1, sizeof(head));
k = -1;
} int main()
{
ios::sync_with_stdio(false); cin.tie(nullptr);
//freopen("T.txt","r",stdin);
cin >> n >> m >> s >> e;
init();
int u,v,w;
for(int i = 1; i <= m; i ++)
{
cin >> u >> v >> w;
Add(u, v, w);
}
cout << max_flow() << endl; return 0;
}

题解二(Dinic)

#include<iostream>
#include<cstring>
#include<queue>
#include<algorithm>
using namespace std; #define INF 1e9
#define ll long long
const int maxn = 100005;
const int maxm = 150000;
int n,m; struct Edge
{
int v;
ll w;
int next;
} edge[maxm];
int head[maxn], cur[maxn];
int deep[maxn]; int k;
void Add(int u, int v, ll w)
{
edge[++ k] = (Edge){ v, w, head[u]};
head[u] = k;
edge[++ k] = (Edge){ u, 0, head[v]};
head[v] = k;
}
//bfs 为图分层 bool bfs(int s, int e)
{
memset(deep, 0x7f, sizeof(deep));
for(int i = 1; i <= n; i ++) cur[i] = head[i];
deep[s] = 0;
queue<int> q;
q.push(s);
int u,v;
ll w;
while(! q.empty())
{
u = q.front(); q.pop(); for(int i = head[u]; i != -1; i = edge[i].next)
{
v = edge[i].v;
w = edge[i].w;
if(w && deep[v] > INF)
{
deep[v] = deep[u] + 1;
q.push(v);
}
}
}
if(deep[e] >= INF)
return false;
return true;
} //dfs 一次找多条增光路(相当于找了一个增广网。。)
ll dfs(int now, int e, ll limit)
{
if(! limit || now == e) return limit; ll flow = 0, f;
for(int i = cur[now]; i != -1; i = edge[i].next)
{
cur[now] = i; //千万注意这一步的意思啊。。这一步是因为 dfs的特性是每次增广一定是增广,那么下一次就不必再检查它了,而直接看第一个未被检查的边
if(deep[edge[i].v] == deep[now] + 1 && (f = dfs(edge[i].v, e, min(limit, edge[i].w))))
{
flow += f;
limit -= f;
edge[i].w -= f;
edge[i^1].w += f;
if(! limit)
break;
}
}
return flow;
} ll Dinic(int s, int e)
{
ll mx_flw = 0;
while(bfs(s, e))
mx_flw += dfs(s, e, INF);
return mx_flw;
} void init()
{
k = -1;
memset(head, -1, sizeof(head));
} int main()
{
ios::sync_with_stdio(false); cin.tie(0);
//freopen("T.txt","r",stdin);
int s,e;
cin >> n >> m >> s >> e;
init();
int u,v;
ll w;
for(int i = 1; i <= m; i ++)
cin >> u >> v >> w, Add(u, v, w);
cout << Dinic(s, e); return 0;
}

题解三(ISAP)

#include<iostream>
#include<algorithm>
#include<queue>
#include<cstring>
using namespace std; #define INF 1e9
const int maxn = 10005;
const int maxm = 200005;
int n, m, s, e;
int k; struct Edge
{
int v,w,next;
} edge[2 * maxm]; int head[maxn],cur[maxn],deep[maxn];
int last[maxm];
int num[maxm]; // num 桶,用来统计每个分层的节点的数量 void Add(int u, int v, int w)
{
edge[++ k] = (Edge){ v, w, head[u]};
head[u] = k;
edge[++ k] = (Edge){ u, 0, head[v]};
head[v] = k;
} //bfs 用于更新deep层
void bfs(int e)
{
// for(int i = 0; i <= n; i ++)
// cur[i] = head[i], deep[i] = n;
for(int i = 0; i <= m; i ++) cur[i] = head[i];
for(int i = 1; i <= n; i ++) deep[i] = n;
deep[e] = 0;
queue<int> q;
q.push(e);
int u, v, w;
while(! q.empty())
{
u = q.front(); q.pop(); for(int i = head[u]; i != -1; i = edge[i].next)
{
v = edge[i].v;
if(edge[i^1].w && deep[v] == n) //正图 边存在 且 v这个节点没有被求过
{
deep[v] = deep[u] + 1;
q.push(v);
}
}
}
} int Add_flow(int s, int e)
{
int ans = INF;
int now = e;
while(now != s)
{
ans = min(ans, edge[last[now]].w);
now = edge[last[now]^1].v;
}
now = e;
while(now != s)
{
edge[last[now]].w -= ans;
edge[last[now]^1].w += ans;
now = edge[last[now]^1].v;
}
return ans;
} int isap(int s, int e)
{
int now = s; //从起点开始进行操作
bfs(e); //先找出来一条边 被操作的增光路
for(int i = 1; i <= n; i ++) num[deep[i]] ++;
int mx_flw = 0;
while(deep[s] < n)
{
if(now == e) //如果到达汇点直接增广,重新回到源点进行下一轮增广
{
mx_flw += Add_flow(s, e);
now = s;
}
bool has_find = 0;
for(int i = cur[now]; i != -1; i = edge[i].next)
{
if(edge[i].w && deep[now] == deep[edge[i].v] + 1)
{
has_find = 1; //做标记已经找到一种可行路径
cur[now] = i; //优化当前弧
now = edge[i].v;
last[edge[i].v] = i;
break;
}
} if(! has_find)
{
int minn = n - 1;
for(int i = head[now]; i != -1; i = edge[i].next)
if(edge[i].w)
minn = min(minn, deep[edge[i].v]);
if( (-- num[deep[now]]) == 0) break; //gap 优化出现了断层
num[deep[now] = minn + 1] ++;
cur[now] = head[now];
if(now != s)
now = edge[last[now]^1].v;
}
}
return mx_flw;
} void init()
{
k = -1;
memset(head, -1, sizeof(head));
} int main()
{
ios::sync_with_stdio(false); cin.tie(0);
//freopen("T.txt","r",stdin);
cin >> n >> m >> s >> e;
init();
int u,v,w;
for(int i = 1; i <= m; i ++)
cin >> u >> v >> w, Add(u, v, w);
cout << isap(s, e);
return 0;
}

P3376 【模板】网络最大流( Edmonds-krap、Dinic、ISAP 算法)的更多相关文章

  1. P3376 [模板] 网络最大流

    https://www.luogu.org/blog/ONE-PIECE/wang-lao-liu-jiang-xie-zhi-dinic EK 292ms #include <bits/std ...

  2. 【洛谷 p3376】模板-网络最大流(图论)

    题目:给出一个网络图,以及其源点和汇点,求出其网络最大流. 解法:网络流Dinic算法. 1 #include<cstdio> 2 #include<cstdlib> 3 #i ...

  3. 图论算法-网络最大流【EK;Dinic】

    图论算法-网络最大流模板[EK;Dinic] EK模板 每次找出增广后残量网络中的最小残量增加流量 const int inf=1e9; int n,m,s,t; struct node{int v, ...

  4. 「模板」网络最大流 FF && EK && Dinic && SAP && ISAP

    话不多说上代码. Ford-Fulkerson(FF) #include <algorithm> #include <climits> #include <cstdio& ...

  5. 【Luogu P3376】网络最大流

    Luogu P3376 最大流是网络流模型的一个基础问题. 网络流模型就是一种特殊的有向图. 概念: 源点:提供流的节点(入度为0),类比成为一个无限放水的水厂 汇点:接受流的节点(出度为0),类比成 ...

  6. [模板]网络最大流 & 最小费用最大流

    我的作业部落有学习资料 可学的知识点 Dinic 模板 #define rg register #define _ 10001 #define INF 2147483647 #define min(x ...

  7. CQOI2016 不同的最小割 (最小割树模板)(等价流树的Gusfield构造算法)

    题目 最小割树模板 算法详解及证明见: 2016年国家队候选队员论文 <浅谈无向图最小割问题的一些算法及应用--绍兴一中 王文涛> 3.2节 CODE #include <bits/ ...

  8. 网络最大流算法—EK算法

    前言 EK算法是求网络最大流的最基础的算法,也是比较好理解的一种算法,利用它可以解决绝大多数最大流问题. 但是受到时间复杂度的限制,这种算法常常有TLE的风险 思想 还记得我们在介绍最大流的时候提到的 ...

  9. P3376 【模板】网络最大流dinic算法

    P3376 [模板]网络最大流 题目描述 如题,给出一个网络图,以及其源点和汇点,求出其网络最大流. 输入输出格式 输入格式: 第一行包含四个正整数N.M.S.T,分别表示点的个数.有向边的个数.源点 ...

随机推荐

  1. 3,Java中的文件IO流

    1,File类 ··· 概念:File对象可以表示一个文件或目录.可以对其进行增删改查. ··· 常用方法:     File f = new File(".");     判断是 ...

  2. Git建立本地分支和远程分支的映射关系

    git branch -vv:查看本地分支和远程分支的映射关系 在切换分支前,须本地建立新分支,例如:git branch release/v1.1   //本地建立release/v1.1分支成功后 ...

  3. 神奇的 SQL 之扑朔迷离 → ON 和 WHERE,好多细节!

    开心一刻 楼主:心都让你吓出来了! 狮王:淡定,打个小喷嚏而已 前情回顾 神奇的 SQL 之 联表细节 → MySQL JOIN 的执行过程(一)中,我们讲到了 3 种联表算法:SNL.BNL 和 I ...

  4. java算法--稀疏数组

    数据结构必要知识 线性结构 线性结构是最常用的数据结构,数据元素之间存在一对一的线性关系. 线性结构有两种不同的存储结构,即顺序存储结构和链式存储结构.顺序存储的线性表称为顺序表,顺序表中的存储元素是 ...

  5. layui表格数据渲染SpringBoot+Thymeleaf返回的数据时报错(Caused by: org.attoparser.ParseException: Could not parse as expression: ")

    layui table渲染数据时报错(Caused by: org.attoparser.ParseException: Could not parse as expression: ") ...

  6. Linux系统是什么?亲身自学经历分享

    我是数字媒体专业学生,第一次接触LINUX的时候,是大一C语言课程里看到的,书上讲了C语言的发展历史.说到C语言的起源,就离不开UNIX系统.在20世纪60年代,贝尔实验室的研究员Ken Thomps ...

  7. vue缓存当前路由(在输入框中输入信息后,跳转其他路由再回来,仍可看到刚刚输入的内容等)

    缓存路由页面的当前状态:   <transition name="fade" mode="out-in"> <keep-alive> & ...

  8. PPP协议(简述)

    PPP协议(链路层协议):用于点对点信道.互联网用户通常需要连接到某个ISP(运营商)之后才能接入到互联网,PPP协议是用户计算机和ISP(运营商)进行通信时所使用的数据链路层协议.该协议可支持同一时 ...

  9. WPF转换器之值转换器

    WPF有两转转换器,一种是值转换器,另一种多值转换器,在开发过程中经常会从数据拉一些数据过来,比如存储性别的时候往往会用0或1,但在界面上肯定是要显示男或女,那么这个时候就可以用上值转换器 编写转换器 ...

  10. Python基础篇(五)_文件和数据格式化

    Python基础篇_文件和数据格式化 文件的使用:文件打开.关闭.读写 文件打开:通过open()函数打开文件,并返回一个操作文件的变量. 使用语法:<变量名> = (<文件路径以及 ...