您的位置:首页 > 理论基础 > 计算机网络

POJ 1273 Drainage Ditches(网络流模板)

2015-08-12 15:24 399 查看
Description:

Every time it rains on Farmer John's fields, a pond forms over Bessie's favorite clover patch. This means that the clover is covered by water for awhile and takes quite a long time to regrow. Thus, Farmer John has built a set of drainage ditches so that Bessie's clover patch is never covered in water. Instead, the water is drained to a nearby stream. Being an ace engineer, Farmer John has also installed regulators at the beginning of each ditch, so he can control at what rate water flows into that ditch.
Farmer John knows not only how many gallons of water each ditch can transport per minute but also the exact layout of the ditches, which feed out of the pond and into each other and stream in a potentially complex network.
Given all this information, determine the maximum rate at which water can be transported out of the pond and into the stream. For any given ditch, water flows in only one direction, but there might be a way that water can flow in a circle.
Input:

The input includes several cases. For each case, the first line contains two space-separated integers, N (0 <= N <= 200) and M (2 <= M <= 200). N is the number of ditches that Farmer John has dug. M is the number of intersections points for those ditches. Intersection 1 is the pond. Intersection point M is the stream. Each of the following N lines contains three integers, Si, Ei, and Ci. Si and Ei (1 <= Si, Ei <= M) designate the intersections between which this ditch flows. Water will flow through this ditch from Si to Ei. Ci (0 <= Ci <= 10,000,000) is the maximum rate at which water will flow through the ditch.
Output:

For each case, output a single integer, the maximum rate at which water may emptied from the pond.
Sample Input:

5 4
1 2 40
1 4 20
2 4 20
2 3 30
3 4 10

Sample Output:

50

题意:现在有m条渠道,n个渠道可以到达的点,以及每条渠道所能运输水的最大流量,同一条渠道可能有不同的流量,第1个点为水的源点,第n个点为水的汇点,现在问从源点到汇点的最低流量是多少。

1.Edmonds-Karp 最短增广路算法:
BFS:查找每一次构图后能否到达汇点,并在查找的同时记录下每个点的父节点,便于下次构图(添加反边,减小正边)
EK:在能查找到汇点的前提下,找到该条路径中最小值(每段路径中的最小值才是整条路径可以通过的最大值),并利用最小值进行重新构图,将每次查找到的最小值相加便是最终最大的流量


#include<stdio.h>
#include<queue>
#include<string.h>
#include<algorithm>
#define INF 0x3f3f3f3f
#define N 210
using namespace std;

int G

, f
;
int n;

int BFS(int Start, int End) //查找能否从源点到达汇点
{
int i, u;

memset(f, 0, sizeof(f)); //f数组保存一条渠道的始点

queue<int>Q;
Q.push(Start);

while (!Q.empty())
{
u = Q.front(); Q.pop();

if (u == End) return 1;

for (i = 1; i <= n; i++)
{
if (G[u][i] && !f[i]) //如果该渠道还有流量,且还未遍历该点
{
Q.push(i);
f[i] = u; //让其父节点为该渠道的始点,便于添加反边
}
}
}

return 0;
}

int EK(int Start, int End)
{
int Max = 0, Min, i;

while (BFS(Start, End)) //一直查找到不能到达汇点
{
Min = INF;

for (i = End; i != Start; i = f[i])
Min = min(Min, G[f[i]][i]); //计算每一次的路径所能达到的最大流量(取决于每段路径流量最少的那个)
for (i = End; i != Start; i = f[i])
{
G[f[i]][i] -= Min;
G[i][f[i]] += Min; //添加反边
}

Max += Min;
}

return Max; //保存流到汇点的最大流量
}

int main ()
{
int m, a, b, c, ans;

while (scanf("%d%d", &m, &n) != EOF)
{
memset(G, 0, sizeof(G));

while (m--)
{
scanf("%d%d%d", &a, &b, &c);
G[a] += c; //可能存在重复渠道的情况,需要将最大流量加起来,这样才能保证流到汇点的流量最大
}

ans = EK(1, n);

printf("%d\n", ans);
}

return 0;
}


[b]2.Dinic 快速网络流算法(邻接矩阵):


BFS:查找能否到达汇点,并在查找的同时将每点出现在BFS中的层数记录下来

DFS:每次DFS完后,会找到路径中容量最小的一条边,在这条边之前的路径的容量是大于等于这条边的容量的,那么从这条边之前的点,可能引发出别的增广路径,比如说 S -> b -> c -> d -> E 是一条增广路径,容量最小的边是 b -> c,可能存在一条 S -> b -> e -> f -> g -> E这样的增广路径,这样的话,在找到第一条增广路径后,只需要回溯到 b 点,就可以继续找下去了,这样做的好处是,避免了找到一条路径就从头开始寻找另外一条,降低了复杂度(本质是回溯)

Dinic:计算每次重新构图后所有的最小值

#include<stdio.h>
#include<queue>
#include<string.h>
#include<algorithm>
using namespace std;

const int INF=0x3f3f3f3f;
const int N=210;

int G

, layer
, n; //layer数组保存的是该点在进行BFS搜索时在第几层

int BFS(int Start, int End)
{
int i, u;

memset(layer, -1, sizeof(layer)); //每次查询时都重新构图了,所以每次都需要初始化

queue<int>Q;
Q.push(Start);
layer[Start] = 1; //让源点为第一层

while (!Q.empty())
{
u = Q.front(); Q.pop();

if (u == n) return 1; //此时已经达到汇点,可以不用再继续查找

for (i = 1; i <= n; i++)
{
if (layer[i] == -1 && G[u][i]) //要是该点还未遍历且有最大流量,此时该点层数+1
{
layer[i] = layer[u]+1;
Q.push(i);
}
}
}

return 0;
}

int DFS(int u, int mini)
{
int i, ans;

if (u == n) return mini; //如果该点为汇点,说明这条路径的最小值已经找到

for (i = 1; i <= n; i++)
{
if (G[u][i] && layer[i] == layer[u]+1 && (ans = DFS(i, min(mini, G[u][i])))) //ans的值肯定不会是0,是最终这条路径上的最小值
{
G[u][i] -= ans;                                                          //因为ans=0时说明该点不能达到汇点,回溯到for循坏,只有能达到汇点才能回溯到这里,而此时ans就是最小值
G[i][u] += ans; //增加反边

return ans;
}
}

return 0;
}

int Dinic(int Start, int End)
{
int ans = 0, num;

while (BFS(Start, End)) //一直到不能达到汇点为止
{
while (1)
{
num = DFS(1, INF);

if (num == 0) break; //等于0说明不能到达汇点

ans += num; //将每次重新构图后的最小值相加
}
}

return ans; //得到最大流量
}

int main ()
{
int m, a, b, c, ans;

while (scanf("%d%d", &m, &n) != EOF)
{
memset(G, 0, sizeof(G));

while (m--)
{
scanf("%d%d%d", &a, &b, &c);
G[a] += c; //防止有重边
}

ans = Dinic(1, n);

printf("%d\n", ans);
}

return 0;
}


3.[b]Dinic 快速网络流算法(邻接表):


邻接表:

head u v flow next
0 1 2 40 -1
1 2 1 0 -1
2 1 4 20 0
3 4 1 0 -1
4 2 4 20 1
5 4 2 0 3
6 2 3 30 4
7 3 2 0 -1
8 3 4 10 7
9 4 3 0 5

#include<stdio.h>
#include<queue>
#include<string.h>
#include<algorithm>
#define INF 0x3f3f3f3f
#define N 210
using namespace std;

struct node
{
int v, flow, next;
}no[2*N];
int layer
, head
;
int n, k;

void Add(int a, int b, int c) ///建立邻接表
{
no[k].v = b;
no[k].flow = c;
no[k].next = head[a]; ///正边
head[a] = k++;

swap(a, b);

no[k].v = b;
no[k].flow = 0;
no[k].next = head[a]; ///反边
head[a] = k++;
}
int BFS(int Start, int End) ///查找能否到达汇点,并记录每个点出现的层数
{
int i, u, v;
memset(layer, -1, sizeof(layer));
queue<int>Q;
Q.push(Start);
layer[Start] = 1;
while (!Q.empty())
{
u = Q.front(); Q.pop();
if (u == n) return 1;
for (i = head[u]; i != -1; i = no[i].next) ///head[u]是u最后一次出现的位置,每次的i变成i上一次出现的位置,直到位置为-1,即所有节点查询完毕
{
v = no[i].v;
if (layer[v] == -1 && no[i].flow)
{
layer[v] = layer[u] + 1;
Q.push(v);
}
}
}
return 0;
}
int DFS(int Start, int End, int mini) ///mini表示到Start这个点时的最大流量
{
int i, flow = 0, ans, v; ///flow表示从Start这个点所能到达汇点的全部流量(可能有许多分支)
if (Start == End) return mini;
for (i = head[Start]; i != -1; i = no[i].next)
{
v = no[i].v;
if (layer[v] == layer[Start]+1 && no[i].flow)
{
ans = min(no[i].flow, mini-flow); ///比较可以流到该节点的最大流量和该节点能流向其他节点的最大流量,取小值
ans = DFS(v, End, ans);

no[i].flow -= ans;
no[i+1].flow += ans; ///添加反边
flow += ans;

if (flow == mini) break; ///相等表示该点无法在到达汇点,它的流量已经全部用完,结束循环(优化效果)
}
}
if (flow == 0) layer[Start] = -1; ///找不到汇点,将该点抹去,下次查询不会查询此点
return flow;
}
int Dinic(int Start, int End)
{
int ans = 0;
while (BFS(Start, End))
ans += DFS(Start, End, INF);
return ans;
}
int main ()
{
int m, a, b, c, ans;
while (scanf("%d%d", &m, &n) != EOF)
{
memset(head, -1, sizeof(head));
k = 0;
while (m--)
{
scanf("%d%d%d", &a, &b, &c);
Add(a, b, c);
}
ans = Dinic(1, n);
printf("%d\n", ans);
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: