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

网络流最大流算法(ISAP算法及DINIC算法)

2016-08-24 19:25 363 查看
这些算法网络中解释都有,所有以下是根据题目应用,代码中有注释,方便理解,ISAP算法就是通过先bfs一遍建立逆序求层数,然后每次都进行维护求最短路来求增广路径,下次查找建立在上次查找的最小路径的源点进行继续查找,DINIC即是通过顺序,每次找最短路径前都进行一次层次的划分(相当于先找出这次查找可能的最小路径长度),再来求最短路增广路径,直至无法划分层次出现断层。下面用两个方法来做这道题,可以作为模板使用

题目:http://acm.split.hdu.edu.cn/showproblem.php?pid=4280

题意:有N个岛屿,M条路线,每条路线连接两个岛屿,并且每条路有最大客流量,这是无向的,就是可以过去也可以回来,现在求最西边到最东边的岛屿,最多有多少人可以过去,每次保证最东边和最西边岛屿只有一个。显示输入N行x,y左边,后面接着路线和客流量

题解:裸算法,直接求即可,ISAP比较好,时间快

代码:

ISAP算法

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<queue>
#define DIAN_MAX 435050
#define BIAN_MAX 435050
#define INF 0xfffffff

using namespace std;
typedef struct
{
int from,tail,cost,next;
}Edge;
int n,m;//n为点的数量,m为边的数量
Edge edge[BIAN_MAX];  //邻接表存储边
int ecount;         //存储边的当前位置
int head[DIAN_MAX];  //点头数组
int copyhead[DIAN_MAX];//head的复制版
int layer[DIAN_MAX];  //层次数组
int gap[DIAN_MAX];  //gap,用于优化该算法,只要出现断层,就说明该算法可以结束了
int myqueue[DIAN_MAX];  //自定义的队列
int start,tail;      //开始点和终止点
int edgestack[DIAN_MAX];  //自定义栈,用于存放一条起始点到终点的最短路路径
void addEdge(int s,int e,int cost)//针对无向和有向皆可
{
edge[ecount].from=s;
edge[ecount].tail=e;
edge[ecount].cost=cost;
edge[ecount].next=head[s];
head[s]=ecount++;
//反向路径,用于后悔的情况
edge[ecount].from=e;
edge[ecount].tail=s;
edge[ecount].cost=0;
edge[ecount].next=head[e];
head[e]=ecount++;
}
void bfs()  //先进行bfs来分层,逆序来求层次,从最终点到起始点,最终点层次为0
{
memset(gap,0,sizeof(gap));
memset(layer,-1,sizeof(layer));
int first=0,last=0,i,now,next;
myqueue[last++]=tail;
gap[0]=1;   //表示第0层次存在1个点 同理推i
layer[tail]=0; //表示第tail点层次为1
while(first<last)
{
now=myqueue[first++];
first=first%DIAN_MAX;
for(i=head[now];i!=-1;i=edge[i].next)
{
next=edge[i].tail;
//逆推层次
if(edge[i].cost!=0||layer[next]!=-1) continue;
myqueue[last++]=next;
last=last%DIAN_MAX;
layer[next]=layer[now]+1;
gap[layer[next]]++;
}
}
}
int ISAP()
{
int ans=0;
bfs();
memcpy(copyhead,head,sizeof(head));
int top=0,now=start,i,minn,op;
while(layer[start]<n)
{
if(now==tail)//这种情况即表示已经找到了从开始点到终点的一条路径
{
minn=INF;
for(i=0;i<top;i++)//找出路径中的最小距离,用于更新其他路径
{
if(minn>edge[edgestack[i]].cost)
{
minn=edge[edgestack[i]].cost;
op=i;
}
}
ans+=minn;
//用最小路径更新
for(i=0;i<top;i++)
{
edge[edgestack[i]].cost-=minn;
edge[edgestack[i]^1].cost+=minn;
}
//用最小路径的起始点来更新接下来要找的起始点,
//因为减去的边是最小的,所以前面边一定没有剪成0,免去了从新遍历
top=op;
now=edge[edgestack[top]].from;
}
//当出现断层时就直接结束,即从S无法到到T集合
if(now!=tail&&gap[layer[now]-1]==0)
{
break;
}
//找出层次差1即相邻,并且路径剩余量不为0的边
for(i=copyhead[now];i!=-1;i=edge[i].next)
{
if(edge[i].cost!=0&&layer[now]==layer[edge[i].tail]+1) break;
}
//若存在,就记录下点然后继续查找
if(i!=-1)
{
copyhead[now]=i;
edgestack[top++]=i;
now=edge[i].tail;
}
//若不存在,更新现在已经排除的路径,免得之后再找
else
{
//找出相邻的层次最小的点,该点可以到达此点,所以这点最小层次为找到的最小值+1
minn=n;
for(i=head[now];i!=-1;i=edge[i].next)
{
if(edge[i].cost<=0) continue;
if(minn>layer[edge[i].tail]) {minn=layer[edge[i].tail];copyhead[now]=i;}
}
//更新代数的数量
gap[layer[now]]--;
layer[now]=minn+1;
gap[minn+1]++;
//从上一个点继续查找
if(now!=start) now=edge[edgestack[--top]].from;
}
}
return ans;
}
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
memset(head,-1,sizeof(head));
ecount=0;
scanf("%d %d",&n,&m);
int minn=1000000;
int maxx=-1000000;
for(int i=1;i<=n;i++)
{
int x,y;
scanf("%d %d",&x,&y);
if(x<minn){minn=x;start=i;}
else if(x>maxx){maxx=x;tail=i;}
}
for(int i=0;i<m;i++)
{
int s,d,w;
scanf("%d %d %d",&s,&d,&w);
addEdge(s,d,w);
addEdge(d,s,w);
}
printf("%d\n",ISAP());
}
return 0;
}


DINIC算法

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#define BIAN_MAX 400050
#define DIAN_MAX 100050
#define INF 0xfffffff

using namespace std;
int n,m;
int start,tail;
typedef struct
{
int from,to,cost,next;
}Edge;
Edge edge[BIAN_MAX];
int myqueue[BIAN_MAX];
int ecount;
int head[DIAN_MAX];
int layer[DIAN_MAX];
void init()
{
memset(head,-1,sizeof(head));
ecount=0;
}
void addedge (int u, int v, int flow)
{
edge[ecount].to = v;
edge[ecount].next = head[u];
edge[ecount].cost = flow;
head[u] = ecount++;

swap (u, v);

edge[ecount].to = v;
edge[ecount].next = head[u];
edge[ecount].cost = flow;
head[u] = ecount++;

}
bool bfs()//每次更新代数,即是找到最小的从起始到终点的距离,每次从起始点开始
{
memset(layer,0,sizeof(layer));
int first=0,last=0,i,next,now;
layer[start] = 1;
myqueue[last++]=start;
while(first<last)
{
now=myqueue[first++];
if(now==tail) return 1;//若找到了最后一点,直接返回,说明已经找到
first=first%BIAN_MAX;
for(i=head[now];i!=-1;i=edge[i].next)
{
next=edge[i].to;
//每次保证该点没有查找过并且有距离余额
if(!layer[next]&&edge[i].cost>0)
{
layer[next]=layer[now]+1;
if(next==tail) return 1;
myqueue[last++]=next;
last%=BIAN_MAX;
}
}
}
return 0;
}
//通过层次来查找最短的路径
int dfs(int s,int limit)
{
if(s==tail) return limit;
int cost=0;
for(int i=head[s];i!=-1;i=edge[i].next)
{
int next=edge[i].to;
if(layer[next]==layer[s]+1&&edge[i].cost>0)
{
int tmp=dfs(next,min(limit-cost,edge[i].cost));
edge[i].cost-=tmp;
edge[i^1].cost+=tmp;
cost+=tmp;
if(cost==limit) break;
}
}
//优化,当这点不能到任意一点时,排除其他点到这点
if(!cost) layer[s]=0;
return cost;
}
int dinic()
{
int ans=0;
while(bfs())
{
ans+=dfs(start,INF);
}
return ans;
}
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
init();
scanf("%d %d",&n,&m);
int minn=1000000,maxx=-1000000;
for(int i=1;i<=n;i++)
{
int x,y;
scanf("%d %d",&x,&y);
if(x<minn) {minn=x;start=i;}
if(x>maxx) {maxx=x;tail=i;}
}
for(int i=1;i<=m;i++)
{
int s,e,w;
scanf("%d %d %d",&s,&e,&w);
addedge(s,e,w);
}
printf("%d\n",dinic());
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  网络流