最大流模板(sap+gap+当前弧)

来源:互联网 发布:苹果手机测试软件 编辑:程序博客网 时间:2024/04/29 09:11

手抄了书上的模板,将它封装在一个结构体里,以后用起来更方便,对应POJ 1273 模板测试题


#include <cstdio>#include <cstring>#include <climits>#include <algorithm>const int N = 222;int n, m;struct ISAP{int head[N], cnt, ans;int gap[N], curedge[N], d[N], pre[N];// gap:统计高度数量数组;d:距离标号数组;// curedges:当前弧数组;pre:前驱数组 struct node{int cap, to;int next;}edge[N * 10];void initi(){memset(d, 0, sizeof(d));memset(gap, 0, sizeof(gap));memset(pre, -1, sizeof(pre));memset(head, -1, sizeof(head));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;for(i = 1;i <= n;i++)curedge[i] = head[i];//初始化当前弧为第一条邻接边 gap[0] = n;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;}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)//寻找可行弧 break;if(i != -1){curedge[u] = i;pre[edge[i].to] = u;u = edge[i].to;} else {if(--gap[d[u]] == 0)break;curedge[u] = head[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;}};int main(){while(~scanf("%d%d", &m, &n)){int i, a, b, c;ISAP g;g.initi();//算法开始前,一定要初始化 for(i = 0;i < m;i++){scanf("%d%d%d", &a, &b, &c);g.addedge(a, b, c);}printf("%d\n", g.max_flow(1, n));}return 0;}

开始时初始标号用一次bfs求出,然后再跑最大流,效果更好。。

#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){            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];        }    }}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;                }                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)        //寻找可行弧                    break;            if(i != -1){                curedge[u] = i;                pre[edge[i].to] = u;                u = edge[i].to;            } else {                if(--gap[d[u]] == 0)                    break;                curedge[u] = head[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;}




原创粉丝点击