网络流三大算法【邻接矩阵+邻接表】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

邻接矩阵:

 1 //邻接矩阵
 2
 3 #include<stdio.h>
 4 #include<string.h>
 5 #include<algorithm>
 6 #include<queue>
 7 #define mem(a, b) memset(a, b, sizeof(a))
 8 const int inf = 0x3f3f3f3f;
 9 using namespace std;
10
11 int m, n;//m为边的数量, n为点的数量
12 int map[210][210];//存图
13 int flow[210]; //记录bfs查找时的最小边,类似木桶效应
14 int pre[210];
15 queue<int>Q;
16
17 int bfs(int st, int ed)
18 {
19     while(!Q.empty())//队列清空
20         Q.pop();
21     for(int i = 1; i <= n; i ++)//1.点的前驱, 利用前驱来更新路径上的正反向边的所剩容量 2.标记是否已经遍历过
22         pre[i] = -1;
23     flow[st] = inf;//flow数组只需要每次bfs时将源点初始化为inf即可, 不需要全部初始化,因为更新时只跟上一个状态以及map边的信息有关
24     Q.push(st);
25     while(!Q.empty())
26     {
27         int index = Q.front();
28         Q.pop();
29         if(index == ed)//找到一条增广路径
30             break;
31         for(int i = 1; i <= n; i ++)//遍历图
32         {
33             if(pre[i] == -1 && map[index][i] > 0 && i != st)//1.没经过i点 2.边的容量大于0 3.终点不为起点,防止没经过汇点死循环
34             {
35                 flow[i] = min(flow[index], map[index][i]);//找到一个可行流上最小的一条边
36                 pre[i] = index;//记录前驱
37                 Q.push(i);
38             }
39         }
40     }
41     if(pre[ed] == -1)//汇点前驱没被更新说明没找到增广路径
42         return -1;
43     else
44         return flow[ed];
45 }
46
47 int max_flow(int st, int ed)
48 {
49     int inc; //每次bfs查找得到的增量
50     int ans = 0; //记每次的增量之和为答案
51     while((inc = bfs(st, ed)) != -1)
52     {
53         int k = ed; //从汇点往回更新
54         while(k != st)
55         {
56             int last = pre[k];
57             map[last][k] -= inc;
58             map[k][last] += inc;
59             k = last;
60         }
61         ans += inc;
62     }
63     return ans;
64 }
65
66 int main()
67 {
68     while(scanf("%d%d", &m, &n)!=EOF)
69     {
70         mem(map, 0);//图的边容量初始化为0
71         for(int i = 1; i <= m; i ++)
72         {
73             int a, b, c;
74             scanf("%d%d%d", &a, &b, &c);
75             if(a == b)
76                 continue;
77             map[a][b] += c;//网络流单向边,若i到j有多根管子,可看作容量叠加的一根管子
78         }
79         int ans = max_flow(1, n); //源点1到汇点n的最大流
80         printf("%d\n", ans);
81     }
82     return 0;
83 }

邻接表(链式前向星实现)

  1 //链式前向星
  2
  3 #include<stdio.h>
  4 #include<string.h>
  5 #include<algorithm>
  6 #include<queue>
  7 #define mem(a, b) memset(a, b, sizeof(a))
  8 const int inf = 0x3f3f3f3f;
  9 using namespace std;
 10
 11 struct Edge
 12 {
 13     int next, to, val;
 14 }edge[210 * 2];//反向边 开两倍空间
 15
 16 int m, n;
 17 int head[210], cnt, pos[210];
 18 int flow[210];
 19 int pre[210];
 20 queue<int>Q;
 21
 22 void add(int a, int b, int c)
 23 {
 24     edge[cnt].to = b;
 25     edge[cnt].val = c;
 26     edge[cnt].next = head[a];
 27     head[a] = cnt ++;
 28
 29     edge[cnt].to = a;
 30     edge[cnt].val = 0;//反向边容量初始化为0
 31     edge[cnt].next = head[b];
 32     head[b] = cnt ++;
 33 }
 34
 35 int bfs(int st, int ed)
 36 {
 37     mem(pos, -1);
 38     while(!Q.empty())
 39         Q.pop();
 40     for(int i = 1; i <= n; i ++)
 41         pre[i] = -1;
 42     flow[st] = inf;
 43     Q.push(st);
 44     while(!Q.empty())
 45     {
 46         int a = Q.front();
 47         Q.pop();
 48         if(a == ed)
 49             return flow[ed];
 50         for(int i = head[a]; i != -1; i = edge[i].next)
 51         {
 52             int to = edge[i].to;
 53             if(pre[to] == -1 && edge[i].val > 0 && to != st)
 54             {
 55                 flow[to] = min(flow[a], edge[i].val);
 56                 pre[to] = a;
 57                 pos[to] = i;//储存寻找到的路径各边的位置, 用于更新val时参与 ^ 运算
 58                 Q.push(to);
 59             }
 60         }
 61     }
 62     return -1;
 63 }
 64
 65 int max_flow(int st, int ed)
 66 {
 67     int inc;
 68     int ans = 0;
 69     while((inc = bfs(st, ed)) != -1)
 70     {
 71         int k = ed;
 72         while(k != st)
 73         {
 74             edge[pos[k]].val -= inc;//巧用 ^1 运算, 0 ^ 1 = 1, 1 ^1 = 0, 2 ^ 1 = 3, 3 ^ 1 = 2, 4 ^ 1 = 5, 5 ^ 1 = 4.
 75                                     // 所以这里链式前向星必须从0开始存边, 这样的话刚好正反向边与 ^ 运算一一对应,例如找到2边, 那么更新2, 3边, 找到3边,那么更新2, 3边
 76             edge[pos[k] ^ 1].val += inc;
 77             k = pre[k];
 78         }
 79         ans += inc;
 80     }
 81     return ans;
 82 }
 83
 84 int main()
 85 {
 86     while(scanf("%d%d", &m, &n)!=EOF)
 87     {
 88         cnt = 0;
 89         mem(head, -1);
 90         for(int i = 1; i <= m; i ++)
 91         {
 92             int a, b, c;
 93             scanf("%d%d%d", &a, &b, &c);
 94             if(a == b)
 95                 continue;
 96             add(a, b, c);//链式前向星存储的是边的位置, 不需要特别处理重边, 存了的边都会在寻找增广路中被用到
 97         }
 98         int ans = max_flow(1, n);
 99         printf("%d\n", ans);
100     }
101     return 0;
102 }

 2.Ford-Fulkerson

这个算法不常用, 可以用于理解后面的dinic算法, 不是很重要,在同学博客里复制过来,嘿嘿嘿

邻接矩阵

 1 #include<iostream>
 2 #include<string.h>
 3 #include<queue>
 4 using namespace std;
 5
 6 int map[300][300];
 7 int n,m;
 8 bool vis[300];//标记该点有没有用过
 9
10 int dfs(int start,int ed,int cnt)
11 {                                 //cnt是查找到的增广路中流量最小的边
12     if(start == ed)
13         return cnt;        //起点等于终点,即已经查到一条可行增广路
14     for(int i = 1; i <= m; i ++)
15     {                                  //以起点start遍历与它相连的每一条边
16         if(map[start][i] > 0 && !vis[i])
17         {                           //这条边是否可行
18             vis[i] = true;                    //标记已经走过
19             int flow = dfs(i, ed, min(cnt, map[start][i]));//递归查找
20             if(flow > 0)
21             {                   //回溯时更行map,这和EK的迭代更行差不多
22                 map[start][i] -= flow;
23                 map[i][start] += flow;
24                 return flow;
25             }
26         }
27     }
28     return 0;//没找到
29 }
30 int Max_flow(int start, int ed)
31 {
32     int ans = 0;
33     while(true)
34     {
35         memset(vis, false, sizeof(vis));
36         int inc = dfs(start, ed, 0x3f3f3f3f);//查找增广路
37         if(inc == 0)
38             return ans;//没有增广路了
39         ans+=inc;
40     }
41 }
42 int main()
43 {
44     int start, ed, w;
45     while(cin >> n >> m)
46     {
47         memset(map, 0, sizeof(map));
48         for(int i = 0; i < n; i ++)
49         {
50             cin >> start >> ed >> w;
51             if(start == ed)
52                 continue;
53             map[start][ed] += w;
54         }
55         cout<<Max_flow(1,m)<<endl;
56     }
57     return 0;
58 }

邻接表:

不写了, 这个算法不重要 

3.dinic

这个算法是要求掌握。这是求解网络流较高效速度比较快的方法。

算法思想:

在寻找增广路之前进行bfs对边进行分层, 例如有边, 1->2,1->3,2->4, 3->4,2->3.那么分层之后就是第一层为点1,第二层为点2, 3.第三层为点4。然后在寻找增广路径时通过层次访问, 就避免了2->3这条边的访问。提高了效率。

邻接矩阵:

 1 #include<stdio.h>
 2 #include<queue>
 3 #include<string.h>
 4 #include<algorithm>
 5 #define mem(a, b) memset(a, b, sizeof(a))
 6 const int inf = 0x3f3f3f3f;
 7 using namespace std;
 8
 9 int m, n;
10 int map[210][210];
11 int dep[210];//点所属的层次
12 queue<int>Q;
13
14 int bfs(int st, int ed)
15 {
16     if(st == ed)
17         return 0;
18     while(!Q.empty())
19         Q.pop();
20     mem(dep, -1);  //层次初始化
21     dep[st] = 1;   //起点定义为第一层
22     Q.push(st);
23     while(!Q.empty())
24     {
25         int index = Q.front();
26         Q.pop();
27         for(int i = 1; i <= n; i ++)
28         {
29             if(map[index][i] > 0 && dep[i] == -1)
30             {
31                 dep[i] = dep[index] + 1;
32                 Q.push(i);
33             }
34         }
35     }
36     return dep[ed] != -1;//返回是否能成功分层,若无法分层说明找不到增广路径了,
37 }
38
39 int dfs(int now, int ed, int cnt)
40 {
41     if(now == ed)//跳出条件, 找到了汇点,获得一条增广路径
42         return cnt;
43     for(int i = 1; i <= n; i ++)
44     {
45         if(dep[i] == dep[now] + 1 && map[now][i] > 0)
46         {
47             int flow = dfs(i, ed, min(cnt, map[now][i]));
48             if(flow > 0)//这条增广路径上最小的边的flow值来更新整个路径
49             {
50                 map[now][i] -= flow;
51                 map[i][now] += flow;
52                 return flow;
53             }
54         }
55     }
56     return -1;//该种分层已经无法找到增广路径
57 }
58
59 int max_flow(int st, int ed)
60 {
61     int ans = 0;
62     while(bfs(st, ed))
63     {
64         while(1)
65         {
66             int inc = dfs(st, ed, inf);
67             if(inc == -1)
68                 break;
69             ans += inc;
70         }
71     }
72     return ans;
73 }
74
75 int main()
76 {
77     while(scanf("%d%d", &m, &n)!=EOF)
78     {
79         mem(map, 0);
80         for(int i = 1; i <= m; i ++)
81         {
82             int a, b, c;
83             scanf("%d%d%d", &a, &b, &c);
84             if(a == b)
85                 continue;
86             map[a][b] += c;
87         }
88         printf("%d\n", max_flow(1, n));
89     }
90     return 0;
91 }

邻接表(链式前向星实现):

  1 #include<stdio.h>
  2 #include<queue>
  3 #include<string.h>
  4 #include<algorithm>
  5 #define mem(a, b) memset(a, b, sizeof(a))
  6 const int inf = 0x3f3f3f3f;
  7 using namespace std;
  8
  9 int m, n;
 10 int head[210], cnt;
 11 int dep[210];
 12 queue<int>Q;
 13
 14 struct Edge
 15 {
 16     int to, next, val;
 17 }edge[500];
 18
 19 void add(int a, int b, int c)// ^ 运算, 从0开始存边
 20 {
 21     edge[cnt].to = b;
 22     edge[cnt].val = c;
 23     edge[cnt].next = head[a];
 24     head[a] = cnt ++;
 25
 26     edge[cnt].to = a;
 27     edge[cnt].val = 0;//反向边容量初始化 0
 28     edge[cnt].next = head[b];
 29     head[b] = cnt ++;
 30 }
 31
 32 int bfs(int st, int ed)
 33 {
 34     if(st == ed)
 35         return 0;
 36     while(!Q.empty())
 37         Q.pop();
 38     mem(dep, -1);//层次初始化
 39     dep[st] = 1; //第一层定义为 1
 40     Q.push(st);
 41     while(!Q.empty())
 42     {
 43         int index = Q.front();
 44         Q.pop();
 45         for(int i = head[index]; i != -1; i = edge[i].next)
 46         {
 47             int to = edge[i].to;
 48             if(edge[i].val > 0 && dep[to] == -1)
 49             {
 50                 dep[to] = dep[index] + 1;
 51                 Q.push(to);
 52             }
 53         }
 54     }
 55     return dep[ed] != -1;
 56 }
 57
 58 int dfs(int now, int ed, int cnt)
 59 {
 60     if(now == ed)
 61         return cnt;
 62     for(int i = head[now]; i != -1; i = edge[i].next)
 63     {
 64         int to = edge[i].to;
 65         if(dep[to] == dep[now] + 1 && edge[i].val > 0)
 66         {
 67             int flow = dfs(to, ed, min(cnt, edge[i].val));
 68             if(flow > 0)
 69             {
 70                 edge[i].val -= flow;
 71                 edge[i ^ 1].val += flow;
 72                 return flow;
 73             }
 74         }
 75     }
 76     return -1;
 77 }
 78
 79 int max_flow(int st, int ed)
 80 {
 81     int ans = 0;
 82     while(bfs(st, ed))
 83     {
 84         while(1)
 85         {
 86             int inc = dfs(st, ed, inf);
 87             if(inc == -1)
 88                 break;
 89             ans += inc;
 90         }
 91     }
 92     return ans;
 93 }
 94
 95 int main()
 96 {
 97     while(scanf("%d%d", &m, &n)!=EOF)
 98     {
 99         cnt = 0;
100         mem(head, -1);
101         for(int i = 1; i <= m; i ++)
102         {
103             int a, b, c;
104             scanf("%d%d%d", &a, &b, &c);
105             if(a == b)
106                 continue;
107             add(a, b, c);
108         }
109         printf("%d\n", max_flow(1, n));
110     }
111     return 0;
112 }

  

原文地址:https://www.cnblogs.com/yuanweidao/p/10805011.html

时间: 2024-07-30 17:43:27

网络流三大算法【邻接矩阵+邻接表】POJ1273的相关文章

POJ 2387 Til the Cows Come Home dijkstra算法 用邻接表和邻接矩阵

题目如下: Til the Cows Come Home Time Limit: 1000MS        Memory Limit: 65536K Total Submissions: 27726        Accepted: 9353 Description Bessie is out in the field and wants to get back to the barn to get as much sleep as possible before Farmer John wa

图基本算法 图的表示方法 邻接矩阵 邻接表

要表示一个图G=(V,E),有两种标准的表示方法,即邻接表和邻接矩阵.这两种表示法既可用于有向图,也可用于无向图.通常采用邻接表表示法,因为用这种方法表示稀疏图(图中边数远小于点个数)比较紧凑.但当遇到稠密图(|E|接近于|V|^2)或必须很快判别两个给定顶点手否存在连接边时,通常采用邻接矩阵表示法,例如求最短路径算法中,就采用邻接矩阵表示. 图G=<V,E>的邻接表表示是由一个包含|V|个列表的数组Adj所组成,其中每个列表对应于V中的一个顶点.对于每一个u∈V,邻接表Adj[u]包含所有满

最短路径算法之五——邻接表

邻接表 邻接矩阵来存储图的信息相对于非完全图,会浪费大量的空间,同时在求最短路径的时候也会有多余的计算浪费时间. 使用邻接表可以节约这些浪费的时间. 这里介绍的是用数组模拟的邻接表: 定义begin[MAXN],end[MAXN],dis[MAXN],first[MAXN],next[MAXN]五个数组. 例子描述如下: 4 5 1 4 9 4 3 8 1 2 5 2 4 6 1 3 7 Step1: 1 2 3 4 5 begin end dis first -1 -1 -1 -1 -1 ne

数据结构----邻接矩阵-邻接表

要表示一个图G=(V,E),有两种标准的表示方法,即邻接表和邻接矩阵.这两种表示法既可用于有向图,也可用于无向图.通常采用邻接表表示法,因为用这种方法表示稀疏图(图中边数远小于点个数)比较紧凑.但当遇到稠密图(|E|接近于|V|^2)或必须很快判别两个给定顶点手否存在连接边时,通常采用邻接矩阵表示法,例如求最短路径算法中,就采用邻接矩阵表示. #include<stdio.h> #include<string.h> #include <stdlib.h> #define

数据结构学习笔记05图 (邻接矩阵 邻接表--&gt;BFS DFS)

数据结构之图 图(Graph) 包含 一组顶点:通常用V (Vertex) 表示顶点集合 一组边:通常用E (Edge) 表示边的集合 边是顶点对:(v, w) ∈E ,其中v, w ∈ V 有向边<v, w> 表示从v指向w的边(单行线) 不考虑重边和自回路 无向图:边是无向边(v, w) 有向图:边是有向边<v, w> 连通:如果从V到W存在一条(无向)路径,则称V和W是连通的 连通图(Connected Graph):如果对于图的任一两个顶点v.w∈V,v和w都是连通的,则称

&lt;图论入门&gt;邻接矩阵+邻接表

非本人允许请勿转载. 趁热打铁,学会了邻接表把这个总结一下,以及感谢大佬uncle-lu!!!(奶一波)祝早日进队! 首先,图论入门就得是非常基础的东西,先考虑怎么把这个图读进去. 给定一个无向图,如下 怎么把这个图的数据读入进去呢? 把这个图剖析开来看,1连着的是2和3,2连着4和5,3连着6和7,4连着5,5连着8和6,6连着9. 所以这个图可以用一种神奇的东西----邻接矩阵来存.如下,能联通的制为1,不能联通的制为0. 那么可以看出来,这个邻接矩阵光读入的时间复杂度就是O(N2)的了,在

HDU 1285 确定比赛名次 拓扑排序(邻接矩阵 邻接表

确定比赛名次 Time Limit:1000MS     Memory Limit:32768KB     64bit IO Format:%I64d & %I64u Description 有N个比赛队(1<=N<=500),编号依次为1,2,3,....,N进行比赛,比赛结束后,裁判委员会要将所有参赛队伍从前往后依次排名,但现在裁判委员会不能直接获得每个队的比赛成绩,只知道每场比赛的结果,即P1赢P2,用P1,P2表示,排名时P1在P2之前.现在请你编程序确定排名. Input 输

图基本算法 最小生成树 Prim算法(邻接表+优先队列STL)

这篇文章是对<算法导论>上Prim算法求无向连通图最小生成树的一个总结,其中有关于我的一点点小看法. 最小生成树的具体问题可以用下面的语言阐述: 输入:一个无向带权图G=(V,E),对于每一条边(u, v)属于E,都有一个权值w. 输出:这个图的最小生成树,即一棵连接所有顶点的树,且这棵树中的边的权值的和最小. 举例如下,求下图的最小生成树: 这个问题是求解一个最优解的过程.那么怎样才算最优呢? 首先我们考虑最优子结构:如果一个问题的最优解中包含了子问题的最优解,则该问题具有最优子结构. 最小

(转)图算法单源最短路径Dijkstra算法(邻接表/邻接矩阵+优先队列STL)

一.前言 最短路径算法,顾名思义就是求解某点到某点的最短的距离.消耗.费用等等,有各种各样的描述,在地图上看,可以说是图上一个地点到达另外一个地点的最短的距离.比方说,我们把地图上的每一个城市想象成一个点,从一个城市到另一个城市的花费是不一样的.现在我们要从上海去往北京,需要考虑的是找到一条路线,使得从上海到北京的花费最小.有人可能首先会想到,飞机直达啊,这当然是时间消耗最小的方法,但是考虑到费用的高昂,这条线路甚至还不如上海到北京的高铁可取.更有甚者,假设国家开通了从上海到西藏,再从西藏到兰州