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

本文介绍了一种改进的最大流算法——ISAP算法,并提供了完整的C++实现代码。该算法通过预计算初始标号并利用间隙现象来加速最大流的求解过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

手抄了书上的模板,将它封装在一个结构体里,以后用起来更方便,对应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;
}




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值