/*****以下来自<> 王欣*****/
层次图
短路增值算法(MPLA)
算法流程
汇点不在层次图内意味着在剩余图中不存在一条从源点到汇点的路径,即没有增广路。
在程序实现的时候,层次图并不用被“建”出来,我们只需对每个顶点标记层次,增广的时候,判断边是否满足level(u) +1= level(v)这一约束即可。
Dinic算法
Dinic算法的思想也是分阶段地在层次图中增广。
它与最短路径增值算法不同之处是:在Dinic算法中,我们用一个dfs过程代替多次bfs来寻找阻塞流。下面给出其算法步骤:
算法流程
增广过程图解
伪代码描述
在程序里,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;
}