首页 > 代码库 > maxflow1273sap_gap

maxflow1273sap_gap

 

Sapdinic复杂度一样的 

优化:

 

1.      邻接表优化:如果顶点多,往往n^2存不下,这时候就要存边:存每条边的出发点,终点点和价值,然后排序一下,再记录每个出发点的位置。以后要调用从出发点出发的边时候,只需要从记录的位置开始找就可以(其实可以用链表)。优化是时间快空间节省,缺点是编程复杂度将变大,所以在题目允许的情况下,建议使用邻接矩阵。

 

2.      GAP优化:如果一次重标号时,出现断层,则可以证明ST无可行流,此时则可以直接退出算法 

 

3.      当前弧优化:为了使每次打增广路的时间变成均摊O(v),还有一个重要的优化是对于每个点保存“当前弧”:初始时当前弧是邻接表的第一条弧;在邻接表中查找时从当前弧开始查找,找到了一条弧,就把这条弧设为当前弧;改变距离标号时,把当前弧设为邻接表的第一条弧。

 

GAP优化:如果一次重标号时,出现断层,则可以证明ST无可行流,此时则可以直接退出算法,。关于断流:

 

假设第四层节点只有一个z,说明无论有多少条增广路,最终一定要经过z点。那么当z不可使用的时候,必定不会再有增广路。

 

#include <cstdio>

#include <queue>

#include <cstring>

#include <climits>

#include <iostream>

#include <algorithm>

using namespace std;

 

const int N = 100010;

int n, m;

struct Edge{

        int cap, to;

        int next;

}edge[N * 4];

 

int head[N], cnt, ans, des, src;

int gap[N], curedge[N], d[N], pre[N], que[N];

 

void bfs(){

    memset(d, -1, sizeof(d));

    memset(gap, 0, sizeof(gap));

    gap[0] = 1;

    int front = 0, rear = 0;

    d[des] = 0;//从汇点开始

    que[rear++] = des;

    while(front != rear){

        int deq = que[front++];

        front = front % N;

        for (int it = head[deq]; it != -1; it = edge[it].next)

{//head是邻接表形式,查看所有与dep相连的边,shortpath3159有关于邻接表的描述

            int e = edge[it].to;

            if (edge[it].cap != 0 || d[e] != -1)

                continue;

            que[rear++] = e;

            rear = rear % N;

            ++gap[d[e] = d[deq] + 1];//gap[i]表示第i层的节点个数

        }

    }

}

 

struct ISAP{

    // gap:统计高度数量数组;d:距离标号数组;

    // curedges:当前弧数组;pre:前驱数组

    void initi(){

        fill(d + 1, d + n + 1, 0);

        fill(pre + 1, pre + n + 1, -1);

        fill(head + 1, head + n + 1, -1);

        ans = 0; //初始化最大流为0

        cnt = 0;

    }

    void addedge(int a, int b, int c){    //有向图加边。

        edge[cnt].to = b, edge[cnt].cap = c;

        edge[cnt].next = head[a], head[a] = cnt++;

        edge[cnt].to = a, edge[cnt].cap = 0;

        edge[cnt].next = head[b], head[b] = cnt++;

    }

    int max_flow(int start, int end){

        int i, u, tmp, neck;

        bfs();

        for(i = 1;i <= n;i++)

            curedge[i] = head[i];    //初始化当前弧为第一条邻接边

        u = start;

        while(d[start] < n){        //d[start] >= n,网络中肯定出现了gap

            if(u == end)

{            //增广成功,寻找瓶颈边

                int min_flow = INT_MAX;

                for(i = start;i != end;i = edge[curedge[i]].to){//直接从当前弧开始

                    if(min_flow > edge[curedge[i]].cap){

                        neck = i;

                        min_flow = edge[curedge[i]].cap;

                    }

                }

 

                for(i = start;i != end;i = edge[curedge[i]].to){//更新正反向弧流量

                    tmp = curedge[i];

                    edge[tmp].cap -= min_flow;

                    edge[tmp ^ 1].cap += min_flow;

//temp^1:

最低位异或
结果就是10,01,2332
就可以通过这样标记对应的反向边,也就是说,这里的邻接表中两个正、反向弧必须紧挨着存储了吧?也就是必须01是一组正反弧,2,3是一对,4,5是一对

                }

                ans += min_flow;

                u = neck;        //下次增广从瓶颈边开始

            }

 

            for(i = curedge[u];i != -1;i = edge[i].next)//从当前弧开始

                if(edge[i].cap&&d[u] == d[edge[i].to] + 1)//寻找可行弧,u是起点,i是与u点直接相连或间接相连的各个边的序号,各个edge[i]的起点都是u

                    break;//通过当前弧,可以在以后的寻找增广路时,直接跳过不会用到的边(邻接表始终按照一种顺序存放),前面不会用的,后面也不会用。

//因为层级数在bfs确定后就不会被改变,所以层次关系如果第一次不满足以后永远不满足,另外如果是层次满足,但因为权值小于0

            if(i != -1)//那么将来不会变成正的,因为这意味着两次的增广路沿着两个节点换了一个方向,这显然不可能满足两个节点层级的关系,毕竟节点层级

{//永远不会被改变

                curedge[u] = i;//改变u节点的当前弧,即与u节点直接相连的边是第i条边

                pre[edge[i].to] = u;//记录路径

                u = edge[i].to;//寻找下一层次节点

            } else//U节点找不到下一个层级节点

{

                if(--gap[d[u]] == 0)//断流

                    break;

                curedge[u] = head[u];//u节点周围选最小的,这里不是从当前弧开始,因为当前弧也没用。目的是在所有相连的边寻找

                for(tmp = n, i = head[u];i != -1;i = edge[i].next)//同时当前弧也需要重新确定,所以赋值为第一个节点

                    if(edge[i].cap)

                        tmp = std::min(tmp, d[edge[i].to]);

                d[u] = tmp + 1;

                ++gap[d[u]];

                if(u != start)

                    u = pre[u];    //重标号并且从当前点前驱重新增广

            }

        }

        return ans;

    }

}g;

 

int main(){

    int T;

    cin >> T;

    while(T--){

        int i, a, b, c, sd = INT_MAX, dd = INT_MIN, x, y;

        scanf("%d%d", &n, &m);

        g.initi();

        for(i = 1;i <= n;i++){

            scanf("%d%d", &x, &y);

            if(sd > x){

                src = i;

                sd = x;

            }

            if(dd < x){

                des = i;

                dd = x;

            }

        }

        for(i = 0;i < m;i++){

            scanf("%d%d%d", &a, &b, &c);

            g.addedge(a, b, c);

            g.addedge(b, a, c);

        }

        printf("%d\n", g.max_flow(src, des));

    }

    return 0;

}

 

maxflow1273sap_gap