网络流三大算法【邻接矩阵+邻接表】POJ1273
网络流的基本概念跟算法原理我是在以下两篇博客里看懂的,写的非常好。
http://www.cnblogs.com/ZJUT-jiangnan/p/3632525.html
http://www.cnblogs.com/zsboy/archive/2013/01/27/2878810.html
网络流有四种算法, 包括 Edmond-Karp(简称EK), Ford-Fulkerson(简称FF), dinic算法以及SAP算法。
下面我会写出前三种算法的矩阵跟邻接表的形式, 对于第四种以后有必要再补充上, 其中dinic算法是比较高效的算法, 要重点掌握dinc,其他两种我是当做辅助理解网络流的,以后做题还是得练dinic
领接矩阵:适用于稠密图,原因:矩阵大小是 n * n的,若边m << n * n,则矩阵中有很多空位置,造成空间浪费, 所以只有稠密图才适合矩阵写法
邻接表:适用于稀疏图,原因:附加链域,稠密图不适合
附上例题链接:http://poj.org/problem?id=1273
1.Edmond-Karp
邻接矩阵:
- //邻接矩阵
- #include<stdio.h>
- #include<string.h>
- #include<algorithm>
- #include<queue>
- #define mem(a, b) memset(a, b, sizeof(a))
- const int inf = 0x3f3f3f3f;
- using namespace std;
- int m, n;//m为边的数量, n为点的数量
- int map[][];//存图
- int flow[]; //记录bfs查找时的最小边,类似木桶效应
- int pre[];
- queue<int>Q;
- int bfs(int st, int ed)
- {
- while(!Q.empty())//队列清空
- Q.pop();
- for(int i = ; i <= n; i ++)//1.点的前驱, 利用前驱来更新路径上的正反向边的所剩容量 2.标记是否已经遍历过
- pre[i] = -;
- flow[st] = inf;//flow数组只需要每次bfs时将源点初始化为inf即可, 不需要全部初始化,因为更新时只跟上一个状态以及map边的信息有关
- Q.push(st);
- while(!Q.empty())
- {
- int index = Q.front();
- Q.pop();
- if(index == ed)//找到一条增广路径
- break;
- for(int i = ; i <= n; i ++)//遍历图
- {
- if(pre[i] == - && map[index][i] > && i != st)//1.没经过i点 2.边的容量大于0 3.终点不为起点,防止没经过汇点死循环
- {
- flow[i] = min(flow[index], map[index][i]);//找到一个可行流上最小的一条边
- pre[i] = index;//记录前驱
- Q.push(i);
- }
- }
- }
- if(pre[ed] == -)//汇点前驱没被更新说明没找到增广路径
- return -;
- else
- return flow[ed];
- }
- int max_flow(int st, int ed)
- {
- int inc; //每次bfs查找得到的增量
- int ans = ; //记每次的增量之和为答案
- while((inc = bfs(st, ed)) != -)
- {
- int k = ed; //从汇点往回更新
- while(k != st)
- {
- int last = pre[k];
- map[last][k] -= inc;
- map[k][last] += inc;
- k = last;
- }
- ans += inc;
- }
- return ans;
- }
- int main()
- {
- while(scanf("%d%d", &m, &n)!=EOF)
- {
- mem(map, );//图的边容量初始化为0
- for(int i = ; i <= m; i ++)
- {
- int a, b, c;
- scanf("%d%d%d", &a, &b, &c);
- if(a == b)
- continue;
- map[a][b] += c;//网络流单向边,若i到j有多根管子,可看作容量叠加的一根管子
- }
- int ans = max_flow(, n); //源点1到汇点n的最大流
- printf("%d\n", ans);
- }
- return ;
- }
邻接表(链式前向星实现)
- //链式前向星
- #include<stdio.h>
- #include<string.h>
- #include<algorithm>
- #include<queue>
- #define mem(a, b) memset(a, b, sizeof(a))
- const int inf = 0x3f3f3f3f;
- using namespace std;
- struct Edge
- {
- int next, to, val;
- }edge[ * ];//反向边 开两倍空间
- int m, n;
- int head[], cnt, pos[];
- int flow[];
- int pre[];
- queue<int>Q;
- void add(int a, int b, int c)
- {
- edge[cnt].to = b;
- edge[cnt].val = c;
- edge[cnt].next = head[a];
- head[a] = cnt ++;
- edge[cnt].to = a;
- edge[cnt].val = ;//反向边容量初始化为0
- edge[cnt].next = head[b];
- head[b] = cnt ++;
- }
- int bfs(int st, int ed)
- {
- mem(pos, -);
- while(!Q.empty())
- Q.pop();
- for(int i = ; i <= n; i ++)
- pre[i] = -;
- flow[st] = inf;
- Q.push(st);
- while(!Q.empty())
- {
- int a = Q.front();
- Q.pop();
- if(a == ed)
- return flow[ed];
- for(int i = head[a]; i != -; i = edge[i].next)
- {
- int to = edge[i].to;
- if(pre[to] == - && edge[i].val > && to != st)
- {
- flow[to] = min(flow[a], edge[i].val);
- pre[to] = a;
- pos[to] = i;//储存寻找到的路径各边的位置, 用于更新val时参与 ^ 运算
- Q.push(to);
- }
- }
- }
- return -;
- }
- int max_flow(int st, int ed)
- {
- int inc;
- int ans = ;
- while((inc = bfs(st, ed)) != -)
- {
- int k = ed;
- while(k != st)
- {
- edge[pos[k]].val -= inc;//巧用 ^1 运算, 0 ^ 1 = 1, 1 ^1 = 0, 2 ^ 1 = 3, 3 ^ 1 = 2, 4 ^ 1 = 5, 5 ^ 1 = 4.
- // 所以这里链式前向星必须从0开始存边, 这样的话刚好正反向边与 ^ 运算一一对应,例如找到2边, 那么更新2, 3边, 找到3边,那么更新2, 3边
- edge[pos[k] ^ ].val += inc;
- k = pre[k];
- }
- ans += inc;
- }
- return ans;
- }
- int main()
- {
- while(scanf("%d%d", &m, &n)!=EOF)
- {
- cnt = ;
- mem(head, -);
- for(int i = ; i <= m; i ++)
- {
- int a, b, c;
- scanf("%d%d%d", &a, &b, &c);
- if(a == b)
- continue;
- add(a, b, c);//链式前向星存储的是边的位置, 不需要特别处理重边, 存了的边都会在寻找增广路中被用到
- }
- int ans = max_flow(, n);
- printf("%d\n", ans);
- }
- return ;
- }
2.Ford-Fulkerson
这个算法不常用, 可以用于理解后面的dinic算法, 不是很重要,在同学博客里复制过来,嘿嘿嘿
邻接矩阵
- #include<iostream>
- #include<string.h>
- #include<queue>
- using namespace std;
- int map[][];
- int n,m;
- bool vis[];//标记该点有没有用过
- int dfs(int start,int ed,int cnt)
- { //cnt是查找到的增广路中流量最小的边
- if(start == ed)
- return cnt; //起点等于终点,即已经查到一条可行增广路
- for(int i = ; i <= m; i ++)
- { //以起点start遍历与它相连的每一条边
- if(map[start][i] > && !vis[i])
- { //这条边是否可行
- vis[i] = true; //标记已经走过
- int flow = dfs(i, ed, min(cnt, map[start][i]));//递归查找
- if(flow > )
- { //回溯时更行map,这和EK的迭代更行差不多
- map[start][i] -= flow;
- map[i][start] += flow;
- return flow;
- }
- }
- }
- return ;//没找到
- }
- int Max_flow(int start, int ed)
- {
- int ans = ;
- while(true)
- {
- memset(vis, false, sizeof(vis));
- int inc = dfs(start, ed, 0x3f3f3f3f);//查找增广路
- if(inc == )
- return ans;//没有增广路了
- ans+=inc;
- }
- }
- int main()
- {
- int start, ed, w;
- while(cin >> n >> m)
- {
- memset(map, , sizeof(map));
- for(int i = ; i < n; i ++)
- {
- cin >> start >> ed >> w;
- if(start == ed)
- continue;
- map[start][ed] += w;
- }
- cout<<Max_flow(,m)<<endl;
- }
- return ;
- }
邻接表:
- 不写了, 这个算法不重要
3.dinic
这个算法是要求掌握。这是求解网络流较高效速度比较快的方法。
算法思想:
在寻找增广路之前进行bfs对边进行分层, 例如有边, 1->2,1->3,2->4, 3->4,2->3.那么分层之后就是第一层为点1,第二层为点2, 3.第三层为点4。然后在寻找增广路径时通过层次访问, 就避免了2->3这条边的访问。提高了效率。
邻接矩阵:
- #include<stdio.h>
- #include<queue>
- #include<string.h>
- #include<algorithm>
- #define mem(a, b) memset(a, b, sizeof(a))
- const int inf = 0x3f3f3f3f;
- using namespace std;
- int m, n;
- int map[][];
- int dep[];//点所属的层次
- queue<int>Q;
- int bfs(int st, int ed)
- {
- if(st == ed)
- return ;
- while(!Q.empty())
- Q.pop();
- mem(dep, -); //层次初始化
- dep[st] = ; //起点定义为第一层
- Q.push(st);
- while(!Q.empty())
- {
- int index = Q.front();
- Q.pop();
- for(int i = ; i <= n; i ++)
- {
- if(map[index][i] > && dep[i] == -)
- {
- dep[i] = dep[index] + ;
- Q.push(i);
- }
- }
- }
- return dep[ed] != -;//返回是否能成功分层,若无法分层说明找不到增广路径了,
- }
- int dfs(int now, int ed, int cnt)
- {
- if(now == ed)//跳出条件, 找到了汇点,获得一条增广路径
- return cnt;
- for(int i = ; i <= n; i ++)
- {
- if(dep[i] == dep[now] + && map[now][i] > )
- {
- int flow = dfs(i, ed, min(cnt, map[now][i]));
- if(flow > )//这条增广路径上最小的边的flow值来更新整个路径
- {
- map[now][i] -= flow;
- map[i][now] += flow;
- return flow;
- }
- }
- }
- return -;//该种分层已经无法找到增广路径
- }
- int max_flow(int st, int ed)
- {
- int ans = ;
- while(bfs(st, ed))
- {
- while()
- {
- int inc = dfs(st, ed, inf);
- if(inc == -)
- break;
- ans += inc;
- }
- }
- return ans;
- }
- int main()
- {
- while(scanf("%d%d", &m, &n)!=EOF)
- {
- mem(map, );
- for(int i = ; i <= m; i ++)
- {
- int a, b, c;
- scanf("%d%d%d", &a, &b, &c);
- if(a == b)
- continue;
- map[a][b] += c;
- }
- printf("%d\n", max_flow(, n));
- }
- return ;
- }
邻接表(链式前向星实现):
- #include<stdio.h>
- #include<queue>
- #include<string.h>
- #include<algorithm>
- #define mem(a, b) memset(a, b, sizeof(a))
- const int inf = 0x3f3f3f3f;
- using namespace std;
- int m, n;
- int head[], cnt;
- int dep[];
- queue<int>Q;
- struct Edge
- {
- int to, next, val;
- }edge[];
- void add(int a, int b, int c)// ^ 运算, 从0开始存边
- {
- edge[cnt].to = b;
- edge[cnt].val = c;
- edge[cnt].next = head[a];
- head[a] = cnt ++;
- edge[cnt].to = a;
- edge[cnt].val = ;//反向边容量初始化 0
- edge[cnt].next = head[b];
- head[b] = cnt ++;
- }
- int bfs(int st, int ed)
- {
- if(st == ed)
- return ;
- while(!Q.empty())
- Q.pop();
- mem(dep, -);//层次初始化
- dep[st] = ; //第一层定义为 1
- Q.push(st);
- while(!Q.empty())
- {
- int index = Q.front();
- Q.pop();
- for(int i = head[index]; i != -; i = edge[i].next)
- {
- int to = edge[i].to;
- if(edge[i].val > && dep[to] == -)
- {
- dep[to] = dep[index] + ;
- Q.push(to);
- }
- }
- }
- return dep[ed] != -;
- }
- int dfs(int now, int ed, int cnt)
- {
- if(now == ed)
- return cnt;
- for(int i = head[now]; i != -; i = edge[i].next)
- {
- int to = edge[i].to;
- if(dep[to] == dep[now] + && edge[i].val > )
- {
- int flow = dfs(to, ed, min(cnt, edge[i].val));
- if(flow > )
- {
- edge[i].val -= flow;
- edge[i ^ ].val += flow;
- return flow;
- }
- }
- }
- return -;
- }
- int max_flow(int st, int ed)
- {
- int ans = ;
- while(bfs(st, ed))
- {
- while()
- {
- int inc = dfs(st, ed, inf);
- if(inc == -)
- break;
- ans += inc;
- }
- }
- return ans;
- }
- int main()
- {
- while(scanf("%d%d", &m, &n)!=EOF)
- {
- cnt = ;
- mem(head, -);
- for(int i = ; i <= m; i ++)
- {
- int a, b, c;
- scanf("%d%d%d", &a, &b, &c);
- if(a == b)
- continue;
- add(a, b, c);
- }
- printf("%d\n", max_flow(, n));
- }
- return ;
- }
网络流三大算法【邻接矩阵+邻接表】POJ1273的更多相关文章
- 数据结构学习笔记05图 (邻接矩阵 邻接表-->BFS DFS、最短路径)
数据结构之图 图(Graph) 包含 一组顶点:通常用V (Vertex) 表示顶点集合 一组边:通常用E (Edge) 表示边的集合 边是顶点对:(v, w) ∈E ,其中v, w ∈ V 有向边& ...
- hdu 1874 畅通工程(spfa 邻接矩阵 邻接表)
题目链接 畅通工程,可以用dijkstra算法实现. 听说spfa很好用,来水一发 邻接矩阵实现: #include <stdio.h> #include <algorithm> ...
- 第6章 图的学习总结(邻接矩阵&邻接表)
我觉得图这一章的学习内容更有难度,其实图可以说是树结构更为普通的表现形式,它的每个元素都可以与多个元素之间相关联,所以结构比树更复杂,然而越复杂的数据结构在现实中用途就越大了,功能与用途密切联系,所以 ...
- POJ 3013 SPFA算法,邻接表的使用
Big Christmas Tree Time Limit: 3000MS Memory Limit: 131072K Total Submissions: 19029 Accepted: 4 ...
- 图的全部实现(邻接矩阵 邻接表 BFS DFS 最小生成树 最短路径等)
1 /** 2 * C: Dijkstra算法获取最短路径(邻接矩阵) 3 * 6 */ 7 8 #include <stdio.h> 9 #include <stdlib.h> ...
- HDU 1874 畅通工程续(最短路/spfa Dijkstra 邻接矩阵+邻接表)
题目链接: 传送门 畅通工程续 Time Limit: 1000MS Memory Limit: 65536K Description 某省自从实行了很多年的畅通工程计划后,终于修建了很多路. ...
- <图论入门>邻接矩阵+邻接表
非本人允许请勿转载. 趁热打铁,学会了邻接表把这个总结一下,以及感谢大佬uncle-lu!!!(奶一波)祝早日进队! 首先,图论入门就得是非常基础的东西,先考虑怎么把这个图读进去. 给定一个无向图,如 ...
- 最短路径SPFA算法(邻接表存法)
queue <int> Q; void SPFA (int s) { int i, v; for(int i=0; i<=n; i++) dist[i]=INF; //初始化每点i到 ...
- c语言实现迪杰斯特拉算法(邻接表)
储存结构,结构体的定义:(权值w用于表示两点间路径的花费) typedef int Status; typedef struct ENode//图的邻接表定义 { int adjVex;//任意顶点u ...
随机推荐
- learning memchr func
extern void *memchr(const void *buf, int ch, size_t count); 用法:#include <string.h> 功能:从buf所指 ...
- linux终端相关概念解释及描述
基本概念: 1. tty(终端设备的统称): tty一词源于Teletypes,或者teletypewriters,原来指的是电传打字机,是通过串行线用打印机键盘通过阅读和发送信息的东西,后来这东西被 ...
- elastic search&logstash&kibana 学习历程(三)Logstash使用场景和安装部署
Logstash基本介绍和使用场景 自我认为:logstash就是日志的采集收集,日志的搬运工,实时去采集日志.读取不同的数据源,并进行过滤,开发者自定义规范输出到目的地.日志的来源很多,如系统日志, ...
- c++继承子类构造函数问题
c++中子类在继承基类的时候需要在构造函数中初始化变量.如果基类没有构造函数或者只有不带参数的构造函数,那么子类中的构造函数就不需要调用基类的构造函数了. 个人总结了两点子类中构造函数必须调用父类的构 ...
- 1 - ImageNet Classification with Deep Convolutional Neural Network (阅读翻译)
ImageNet Classification with Deep Convolutional Neural Network 利用深度卷积神经网络进行ImageNet分类 Abstract We tr ...
- Leetcode题目39.组合总和(回溯+剪枝-中等)
题目描述: 给定一个无重复元素的数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合. candidates 中的数字可以无 ...
- koa 实现下载文件
文件下载需要使用到koa-send这个插件,该插件是一个静态文件服务的中间件,它可以用来实现文件下载的功能. 1.下载页面 static/download.html <!DOCTYPE html ...
- linux不能ping通主机,主机能ping通linux
1).打开控制面板,点击“系统和安全”选项.然后打开“Windows防火墙”.2).点击进入“高级设置”,选择“入站规则”.3).在入门规则中找到“文件和打印机共享(回显请求-ICMPv4-In)”选 ...
- PHP将多个文件中的内容合并为新的文件
function test() { $hostdir= iconv("utf-8","gbk","C:\Users\原万里\Desktop\日常笔记& ...
- mongodb 安装配置及简单使用
步骤一: 下载网址:https://www.mongodb.com/download-center/community 根据自己的环境下载 步骤二: 安装过程只需要默认即可,需要注意的是连接工具“mo ...