您的位置:首页 > 大数据 > 人工智能

[hdu 1532] Drainage Ditches(最大流dinic)

2014-04-17 14:57 405 查看
算是一个网络流入门介绍贴吧,这里就不说各种剩余图,层次图的概念了,这些网上太多了,这里直接说些实在点的东西吧。

网络:的意思是给定一个网络(有向图),这个网络中有起点也有终点,各条边上的流量表示最大值,也就是有多少单位的东西能够经过这条边。

最大流是网络流的其中一类问题,求的是从起点出发,最终有多少个"货物"能到达终点。以一个图来理解一下这个最大流:



这个图的答案应该是9,1->2->4->5能运送4个货物,1->3->5能运送5个货物,所以到了终点一共是9个货物。有必要补充几点:

1.一条路上能运送的最大数量取决去这条路上运送能力最弱的路段,即数值最小的边。这句话对稍后的编程非常重要。

2.起点运送到终点,所以可以看成起点是仓库(起点能运出来无穷多的货物),终点也是仓库(起点能够运的过来多少东西,终点就能接收多少东西),唯一受限制的就是每个路段的运送能力了。

回到这个题目,首先先将题目抽象:

        告诉有n条边,共m个点(1~m),然后是n条边的头尾与流量。1是起点,m是终点,问能运达终点的最大流量是多少。

这里简要介绍一下dinic算法:

1.用bfs对各顶点标号,计算出层次图,如果层次图中没有出现终点,那么说明最大流计算完毕。

        层次图的计算方法为:设一个dep数组表示各结点的层次(可以理解成从起点出发的深度)。从已访问过的结点出发,如果从这个点出去的没访问的边的流量大于零,那么这条边的终点那个结点的层次为起点的层次+1.

int bfs() // 建立层次图
{
queue<int> q;
memset(dep, -1, sizeof(dep)); // 初始化结点的层次
dep[st] = 0; // 从起点开始,起点层次为0(深度0)
q.push(st);
while(!q.empty())
{
int cur = q.front();
q.pop();
for(int i = head[cur]; i != -1; i = edge[i].next)
{
int s = edge[i].t;
if(dep[s] == -1 && edge[i].flow > 0) // 未访问过, 且这条边的流量大于零
{
dep[s] = dep[cur] + 1;
q.push(s);
}
}
}
// 若终点不在层次图中, 说明bfs的时候没访问到终点
// 也就是说通向终点的边的流量都变成0了
return dep[end] != -1;
}


2.用dfs找出一条通往终点的可行的路,这条路称为增广路,设这条路上流量最小值为x,那么整条路每个路段流量减去x,答案加上x。

int dfs(int st, int limit) // 起点和限制流量(从起点到当前的边的最小流量)
{
int x;
if(st == end) // 当走到终点时,一路下来的最小值就是答案, 所以返回limit
return limit;
for(int i = head[st]; i != -1; i = edge[i].next)
{
int s = edge[i].t;
if(edge[i].flow > 0 && dep[s] == dep[st] + 1) // 一路过来, 层次逐渐加深
{
x = dfs(s, min(limit, edge[i].flow));
if(x == 0)continue; // 这条路不通, 找下一条路
edge[i].flow -= x;
return x;
}
}
return 0;
}
3.转步骤2继续构建层次图和寻找增广路,直到增广完毕。

分析一下这道题的样例数据,首先构图(起点和终点用绿色以示突出):



第一步是构建层次图(红色为层次):



dfs寻找增广路,找到了:



目前为止知道至少有20的流量能到终点了,然后在原图中减掉这条路的流量,为了方便,如果哪条路流量为0就不再画出。



继续建立层次图:



找增广路:



目前为止已有20+现在的20=40的流量能到终点了,继续更新原图。



建立层次图以及寻找增广路:



20+20+10 = 50

现在的图:



增广完毕,答案为50.

其实还有个反向边的概念,上图的数据中因为没用到所以没有画出来,不过这是不可忽略的。本文已十分冗长,只是起个抛砖引玉的作用,关于反向边的概念请另行搜索网络流神牛们的文章。

附上完整程序:

#include<iostream>
#include<cstdio>
#include<queue>
#include<cstring>
using namespace std;

#define maxn 210
#define inf 0x3f3f3f3
struct Edge
{
int t;
int flow;
int next;
}edge[maxn * 3];

int head[maxn];
int dep[maxn];
int n, m;
int st, end;
int edgenum;

int min(int a, int b)
{
return a < b ? a : b;
}

void addedge(int s, int t, int flow)
{
edge[edgenum].t = t;
edge[edgenum].flow = flow;
edge[edgenum].next = head[s];
head[s] = edgenum++;
}

int bfs() // 建立层次图 { queue<int> q; memset(dep, -1, sizeof(dep)); // 初始化结点的层次 dep[st] = 0; // 从起点开始,起点层次为0(深度0) q.push(st); while(!q.empty()) { int cur = q.front(); q.pop(); for(int i = head[cur]; i != -1; i = edge[i].next) { int s = edge[i].t; if(dep[s] == -1 && edge[i].flow > 0) // 未访问过, 且这条边的流量大于零 { dep[s] = dep[cur] + 1; q.push(s); } } } // 若终点不在层次图中, 说明bfs的时候没访问到终点 // 也就是说通向终点的边的流量都变成0了 return dep[end] != -1; }

int dfs(int st, int limit) // 起点和限制流量(从起点到当前的边的最小流量)
{
int x;
if(st == end) // 当走到终点时,一路下来的最小值就是答案, 所以返回limit
return limit;
for(int i = head[st]; i != -1; i = edge[i].next)
{
int s = edge[i].t;
if(edge[i].flow > 0 && dep[s] == dep[st] + 1) // 一路过来, 层次逐渐加深
{
x = dfs(s, min(limit, edge[i].flow));
if(x == 0)continue; // 这条路不通, 找下一条路
edge[i].flow -= x;
edge[i^1].flow += x;
return x;
}
}
return 0;
}

int dinic()
{
int flow = 0, t;
while(bfs())
{
flow += dfs(st, inf);
}
return flow;
}

int main()
{
int a, b, c;
while(~scanf("%d%d", &n, &m))
{
edgenum = 0;
memset(head, -1, sizeof(head));
for(int i = 0; i < n; i++)
{
scanf("%d%d%d", &a, &b, &c);
addedge(a, b, c);
addedge(b, a, 0);
}
st = 1, end = m;
int re = dinic();
printf("%d\n", re);
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: