最大流的非递归Dinic算法
Dinic是在Edmond_Karp上的优化,就是传说中的分层;分层听起来难理解,其实就是分级即给节点具有一定规则的标记,看一下实现就懂了!
算法的实现步骤:
1.分层:利用BFS搜索方式给每个节点给予标记level[i];
2.判断分层是否成功即汇点是否被分到级别level[sink]!=0;
3.在分层的基础上即level[i]上寻找所有的增广路、累计流量,回到步骤1;
求最大流的非递归Dinic算法code:
View Code
//--------Dinic的非递归正向实现---------
#define NOT(x) (x&1?x+1:x-1)
struct Edge
{
int u;
int value;
int next;
}edge[MAXM*2];
int level[MAXN],queue[MAXN],node[MAXN],variednode[MAXN];//level给每一个节点分级,即分层;queue分层时当队列用,找增广路时
int front,rear;//当栈用;node[i]为结点i的指针;variednode可变的结点指针(原本是node的复制,但是他不断变化);front,rear
int index;//即队列的首尾;top即栈顶;index即作为edge的下标;
int top;
void Build_Graph(int m)//建图即建立网络
{
int v,u,value;
index=0; memset(node,-1,sizeof(node));
for(int i=0;i<m;i++)
{
scanf("%d %d %d",&v,&u,&value);
++index; //这里用数组模拟指针,模拟链表结构
edge[index].u=u; //重在自己慢慢体会,讲不出来效果的……
edge[index].value=value;
edge[index].next=node[v];
node[v]=index;/////建立原网络
++index;
edge[index].u=v;
edge[index].value=0;
edge[index].next=node[u];
node[u]=index;/////建立反网络
}
}
int Dinic(int source,int sink,int n)
{
int maxflow=0;
int v,u,value;
while(true)
{
memset(level,0,sizeof(level));
front=rear=0; level[source]=1;
queue[0]=source;
while(front<=rear)/////传说中的分层
{
v=queue[front++];//注意这里的queue当队列用储存的是结点
for(int i=node[v];i!=-1;i=edge[i].next)
{
u=edge[i].u; value=edge[i].value;
if(value && level[u]==0)
{
level[u]=level[v]+1;
queue[++rear]=u;
if(u==sink) break;
}
}
if(u==sink) break;
}
if(level[sink]==0) break;//这个就是判断是否存在增广路,没有就结束了
for(int i=1;i<=n;i++) variednode[i]=node[i];//看variednode——node的复制,以后就不断变化。这就是优化,记录下次开始访问的边。
edge[0].u=source; top=0; queue[++top]=0;//这里的所做是为了“凑”下边的循环,这里的queue做栈用储存边的下标
while(top)//////求该分层下的最短增广路
{
int i; v=edge[queue[top]].u;
for(i=variednode[v];i!=-1;i=edge[i].next)
{
u=edge[i].u; value=edge[i].value;
if(value && level[u]==level[v]+1)
{
queue[++top]=i;
variednode[v]=edge[i].next;
break;
}
}
if(i==-1) { variednode[v]=-1;top--;continue; }//若该点四周不存在最短增广路,则直接variednode[v]=-1;以防下次做
//多余的查找。top--退一条边,再找
if(u==sink)//找到一条边就判断一下该点是不是sink,不是继续向下找
{
int min=0x7fffffff,flag;
for(i=2;i<=top;i++)
if(min>edge[i].value)
min=edge[i].value;//找到最大流量
for(i=top;i>1;i--)
{
if(min==edge[queue[i]].value)
flag=i;//更新该正向路径的各个流量,并标记出第一个流量变为0的边所在的栈位
edge[queue[i]].value-=min;
edge[NOT(queue[i])].value+=min;//看看怎么建图的吧 }
top=flag-1;//更新top
maxflow+=min;//更新maxflow
}
}
}
return maxflow;
}
这啥玩意缩进啊,草!气死我了,整了半天还这样……
上边的代码,主要是第三步,我想了想感觉有可能会做太多的无用功——在level[i]的基础上找增广路,他是用DFS的方式查找增广路。他是从原点向汇点找,刚开始找到的边(符合条件的边)可能就不在增广路上,可是却要找到头才知道不是增广路,然后在一步步往回退,这岂不是做了大量的无用功?!感觉这样太盲目了~于是想了想:为什么不从汇点向原点找?从汇点向原点不是盲目的,刚开始找到的边(符合条件的边)一定在增广路上!这样就不会做像从原点到汇点找一样做许多无用功!!两段代码都差不多,具体细节看代码:
View Code
struct Edge
{
int u;
int value;
int next;
}edge[MAXM];
int node[MAXN],variednode[MAXN];
int level[MAXN],queue[MAXN];
int front,rear,top,index;
void Build_Graph(int m)
{
int v,u,value;
memset(node,-1,sizeof(node)); index=0;
for(int i=0;i<m;i++)
{
scanf("%d %d %d",&v,&u,&value);
index++;
edge[i].u=u;
edge[i].value=value;
edge[i].next=node[v];
node[v]=index;
index++;
edge[i].u=v;
edge[i].value=0;
edge[i].next=node[u];
node[u]=index;
}
}
int Dinic(int source,int sink,int n)
{
int max_flow=0;
int i,v,u,value;
while(true)
{
front=rear=0;
queue[rear]=source;
memset(level,0,sizeof(level)); level[source]=1;
while(front>=rear)
{
v=queue[front++];
for(i=node[v];i!=-1;i=edge[i].next)
{
u=edge[i].u; value=edge[i].value;
if(value && level[u]==0)
{
level[u]=level[v]+1;
queue[++rear]=u;
if(u==sink) break;
}
if(u==sink) break;
}
}
if(level[sink]==0) break;
for(i=1;i<=n;i++) variednode[i]=node[i];
edge[0].u=sink; top=0; queue[++top]=0;
while(top)
{
v=edge[queue[top]].u;
for(i=variednode[v];i!=-1;i=edge[i].next)
{
u=edge[i].u; value=edge[NOT(i)].value;
if(value && level[v]=level[u]+1)
{
variednode[v]=edge[i].next;
queue[++top]=NOT(i);
break;
}
}
if(i==-1)
{
variednode[v]=-1;
top--; continue;
}
if(u==source)
{
int min=0x7fffffff,flag;
for(i=2;i<=top;i++)
{
if(min>edge[queue[top]].value)
min=edge[queue[top]].value;
}
for(i=top;i>1;i--)
{
if(edge[queue[top]].value==min)
flag=i;
edge[queue[top]].value-=min;
edge[NOT(queue[top])].value+=min;
}
top=flag;
max_flow+=min;
}
}
}
}
//------Dinic的非递归的反向实现------
#define NOT(x) ( (x&1)?(x+1):(x-1) ) //取反边struct Edge
{
int u;
int value;
int next;
}edge[MAXM];
int node[MAXN],variednode[MAXN];
int level[MAXN],queue[MAXN];
int front,rear,top,index;
void Build_Graph(int m)
{
int v,u,value;
memset(node,-1,sizeof(node)); index=0;
for(int i=0;i<m;i++)
{
scanf("%d %d %d",&v,&u,&value);
index++;
edge[i].u=u;
edge[i].value=value;
edge[i].next=node[v];
node[v]=index;
index++;
edge[i].u=v;
edge[i].value=0;
edge[i].next=node[u];
node[u]=index;
}
}
int Dinic(int source,int sink,int n)
{
int max_flow=0;
int i,v,u,value;
while(true)
{
front=rear=0;
queue[rear]=source;
memset(level,0,sizeof(level)); level[source]=1;
while(front>=rear)
{
v=queue[front++];
for(i=node[v];i!=-1;i=edge[i].next)
{
u=edge[i].u; value=edge[i].value;
if(value && level[u]==0)
{
level[u]=level[v]+1;
queue[++rear]=u;
if(u==sink) break;
}
if(u==sink) break;
}
}
if(level[sink]==0) break;
for(i=1;i<=n;i++) variednode[i]=node[i];
edge[0].u=sink; top=0; queue[++top]=0;
while(top)
{
v=edge[queue[top]].u;
for(i=variednode[v];i!=-1;i=edge[i].next)
{
u=edge[i].u; value=edge[NOT(i)].value;
if(value && level[v]=level[u]+1)
{
variednode[v]=edge[i].next;
queue[++top]=NOT(i);
break;
}
}
if(i==-1)
{
variednode[v]=-1;
top--; continue;
}
if(u==source)
{
int min=0x7fffffff,flag;
for(i=2;i<=top;i++)
{
if(min>edge[queue[top]].value)
min=edge[queue[top]].value;
}
for(i=top;i>1;i--)
{
if(edge[queue[top]].value==min)
flag=i;
edge[queue[top]].value-=min;
edge[NOT(queue[top])].value+=min;
}
top=flag;
max_flow+=min;
}
}
}
}
我感觉这样反着搜索比较好,可是是否会存在一些漏洞?不知道,至少我现在没发现;如果大虾你发现有什么不对,望能给我指正!十分感谢!!