dinic算法 c 语言,网络流Dinic算法详解及模板

本文深入探讨了网络流问题中的Dinic算法和层次图短路增值算法(MPLA)。Dinic算法通过深度优先搜索(DFS)寻找增广路径,而MPLA算法利用层次图进行增广。两种算法都在寻找从源点到汇点的路径以最大化流。Dinic算法以更高效的方式寻找阻塞流,而MPLA算法则依赖于层次结构。通过实例和伪代码,作者清晰地解释了这两种算法的工作原理和实现细节。
摘要由CSDN通过智能技术生成

/*****以下来自<> 王欣*****/

层次图

836b6beea173207e582e9c02e08850d1.png

c164408feca3e8fb1d86f034ccdfd8c5.png

短路增值算法(MPLA)

算法流程

b00c8610c9c74855151b1497914c3b6f.png

汇点不在层次图内意味着在剩余图中不存在一条从源点到汇点的路径,即没有增广路。

在程序实现的时候,层次图并不用被“建”出来,我们只需对每个顶点标记层次,增广的时候,判断边是否满足level(u) +1= level(v)这一约束即可。

Dinic算法

Dinic算法的思想也是分阶段地在层次图中增广。

它与最短路径增值算法不同之处是:在Dinic算法中,我们用一个dfs过程代替多次bfs来寻找阻塞流。下面给出其算法步骤:

算法流程

9177e6d5000af09691fcffb84c1f3a43.png

增广过程图解

53f8bbcdb3f2b88eb0d59f218b1919ab.png

28efa5d67490be8195dbb7f7a402daaf.png

伪代码描述

a101ec9c57a455e8d75e433c79e83c06.png

在程序里,p表示找到的增广路径,p.top为路径中的最后一个顶点。一开始,p中只有源点。

整个While循环分为2个操作。如果p的最后一个顶点为汇点,也就是说找到了增广路,那么对p增广,注意到增广后一定有一条或多条p中的边被删除了。这时,我们使增广路径后退至p中从源点可到达的最后一个顶点。

如果p的最后一个顶点不为汇点,那么观察最后那个的顶点u 。若在层次图中存在从u连出的一条边,比如(u,v),我们就将顶点v放入路径p中,继续dfs遍历;否则,点u对之后的dfs遍历就没有用了,我们将点u以及层次图中连到u的所有边删除,并且在p中后退一个点。

Dfs过程将会不断重复这2个操作,直到从源点连出的边全部被删除为止。

/*****以上来自<> 王欣*****/

模板

/* filename :network_flow_Dinic.cpp

* author :AntiheroChen

* Description :It's a Dinic solution for the network flow prblem.

* Complexity :O(V^2*E) always below this.

* Version :1.00

* History :

* 1)2012/02/29 first release.

*/

#include

#include

#include

#include

using namespace std;

const int maxn=1000+5, bign=1000000000;

int M, n, m, source, sink, c[maxn][maxn], cnt[maxn];

/* The arc of the flow network.*/

struct Pool

{

int next, t, c;

} edge[maxn*maxn<<1];

/* The point of the flow network.*/

struct Point

{

int son, cur, pre, lim, d;

} a[maxn];

/* Prepare for the algorithm.*/

void initialize()

{

M=1;

memset(c, 0, sizeof (c));

memset(a, 0, sizeof (a));

memset(cnt, 0, sizeof (cnt));

}

/* Add an arc to the flow network.*/

void add(int x, int y, int z)

{

edge[++M].t=y;

edge[M].c=z;

edge[M].next=a[x].son;//相当于pool的head数组

a[x].son=M;

}

/* Read the data and make it the right format.*/

void input()

{

scanf("%*s%*d%d%d%d%d", &n, &m, &source, &sink);

initialize();

int x, y, z;

while (m--)

scanf("%d%d%d", &x, &y, &z), c[x][y]+=z;

for (int i=0; i

for (int j=0; j

if (c[i][j])add(i, j, c[i][j]), add(j, i, c[j][i]), c[j][i]=0;

}

int que[maxn], fi, la;

bool vis[maxn];

/* Build the hierarchical graph for the algorithm*/

bool build()

{

memset(vis, 0, sizeof (vis));

que[fi=la=0]=sink;//reverse

a[sink].d=0, a[sink].cur=a[sink].son, vis[sink]=true;

while (fi<=la)

{

int v=que[fi++];

for (int now=a[v].son, u; u=edge[now].t, now; now=edge[now].next)

if (edge[now^1].c&&!vis[u])//BFS来分层,这里和EK相同

{//倒着BFS的话,当然引用的还是对侧边,即正向边

a[u].d=a[v].d+1;//越向前标号渐大

a[u].cur=a[u].son;//cur指向头

vis[u]=true;//已遍历

que[++la]=u;//入队

}

if (vis[source])return true;//层次图向前已经扩展到原点

}

return false;

}

/*Use the Dinic algorithm to calculate the max flow.*/

int MaxFlow()

{

int u, v, now, ret=0;

while (build())

{

a[u=source].lim=bign;

while (true)

{

for (now=a[u].cur; v=edge[now].t, now; now=edge[now].next)//cur优化

if (edge[now].c&&a[u].d==a[v].d+1)break;//找到了一个子节点属于层次图

if (now)

{

a[u].cur=edge[now].next;//下一次从这一条边的下一条边开始dfs

a[v].pre=now;//指向v的边的指针

a[v].lim=min(a[u].lim, edge[now].c);///更新到此处为止流的上限

if ((u=v)==sink)//如果已经找到了一条增广路(走到了尽头)

///注意这个地方借判断语句, 将u下移, 便于判断为否的时候回到上面进入下一层!

{//进行增广

do

{

edge[a[u].pre].c-=a[sink].lim;

edge[a[u].pre^1].c+=a[sink].lim;//这两句和Edmonds-Karp是一样的,增广

u=edge[a[u].pre^1].t;//找前驱~!

} while (u!=source);

ret+=a[sink].lim;//增广完毕之后累加新找到的流

}//否则(没走到尽头)继续向下DFS

}

else//没有子节点属于层次图

{

if (u==source)break;//已经退到了源,则已找到最大流,算法结束

a[u].cur=now;//=0,此节点被废弃,子代亦然

u=edge[a[u].pre^1].t;//根据反向边找到前驱~!

}

}

}

return ret;

}

int main()

{

int total;

scanf("%d", &total);

while (total--)

{

input();

printf("%d\n", MaxFlow());

}

return 0;

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值