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

来源:互联网 发布:奥尔弗斯 知乎 编辑:程序博客网 时间:2024/06/06 04:58

这些算法网络中解释都有,所有以下是根据题目应用,代码中有注释,方便理解,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 0xfffffffusing 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 0xfffffffusing 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;}


0 0
原创粉丝点击