网络流(模版)

参考博文

只列出几种常见的,快的网络流算法

1.Dinic

struct Edge{
    int v, w, nxt;
};
Edge edge[maxn << 1];
int pre[maxn], tot, dis[maxn << 1], cur[maxn];

void init() {
    tot = 0;
    memset(edge, 0, sizeof(edge));
    memset(pre, -1, sizeof(pre));
}

void addEdge(int s, int e, int w) {
    edge[tot].v = e;
    edge[tot].w = w;
    edge[tot].nxt = pre[s];
    pre[s] = tot ++;
}

int n, m, s, e;

int bfs() {
    memset(dis, -1, sizeof(dis));
    dis[e] = 0;
    queue<int> que;
    que.push(e);
    while(!que.empty()) {
        int u = que.front(); que.pop();
        for (int i = pre[u]; i + 1; i = edge[i].nxt) {
            if(dis[edge[i].v] == -1 && edge[i ^ 1].w > 0) {
                dis[edge[i].v] = dis[u] + 1;
                que.push(edge[i].v);
            }
        }
    }
    return dis[s] != -1;
}

int dfs(int u, int flow) {
    if(u == e) return flow;
    int belta = flow;//belta 可以看作是从源点到此处的最大容量
    for (int &i = cur[u]; i + 1; i = edge[i].nxt) {
    //cur[i] = i;
        if(dis[u] == dis[edge[i].v] + 1 && edge[i].w > 0) {
            int d = dfs(edge[i].v, min(belta, edge[i].w));
            edge[i].w -= d; edge[i ^ 1].w += d;
            belta -= d;
            if(belta == 0) break;//如果容量为0,那么这条路就不再有增广路,break
        }
    }
    return flow - belta;//返回的是 最大容量-剩余容量
}

int dinic() {
    int ans = 0;
    while(bfs()) {
       for (int i = 1; i <= n; i ++) 
           cur[i] = pre[i];
         ans += dfs(1, INF);
    }
    return ans;
}


int main(int argc, const char * argv[]) {
    int T, karse = 0;
    scanf("%d", &T);
    while(T --) {
        init();
        scanf("%d %d", &n, &m);
        for (int i = 0; i < m; i ++) {
            int u, v, w;
            scanf("%d %d %d", &u, &v, &w);
            addEdge(u, v, w);
            addEdge(v, u, 0);
        }
        s = 1; e = n;
        printf("Case %d: %d\n", ++karse, dinic());
    }

其实SAP与Dinic本质都是一样的,都是对图进行分层,不过Dinic是每跑一次都对dis[]重新编号,这个在数据少的情况下很好用,但数据多的话就会有超时的危险,而SAP是只进行一次BFS,其余情况用gap数组来记录距离汇点的距离,如果某一个距离为0,那么这个图就不再有增广路。
如图:
初始的分层是这样的,1是源点,6是汇点
这里写图片描述
经过一次查找后
这里写图片描述
而此时2与3联通,但是不满足dis[2] == dis[3] + 1。这时
这里写图片描述
而此时1又与2的dis相同,所以1也+1
这里写图片描述
其余的与这个类似

2.SAP

SAP主函数


//si为源点,ei为汇点
void SAP(int s){
    int dx, augc, minDis;
    if(s == ei){
        isFind = true;
        maxFlow += aug;
        return;
    }
    augc = aug;
    minDis = n - 1;//n个点最大有n-1层
    for(int i = pre[s]; i + 1; i = edge[i].nxt){
        if(edge[i].w > 0){
            if(dis[s] == dis[edge[i].v] + 1){//可行边
                aug = min(aug, edge[i].w);
                SAP(edge[i].v);//向下 找
                if(dis[si] >= n) return;//如果起点的dis>=n,说明有断层直接退出
                if(isFind){
                    dx = i;
                    break;
                }
                aug = augc;
            }
            minDis = min(minDis, dis[edge[i].v]);//找到层数最小的
        }
    }
    if(!isFind){//如果没找到
        gap[dis[s]]--;//距离为dis[s]的数量--
        if(gap[dis[s]] == 0) dis[si] = n;//有距离汇点dis[s]的点的数量为0,那么已经没有增广路了
        dis[s] = minDis + 1;//对于当前的s,距离为minDis+1
        gap[dis[s]]++;//gap[dis[s]] ++
    }else{//如果找到了,那就
        edge[dx].w -= aug;
        edge[dx ^ 1].w += aug;
    }
}

bfs函数

void bfs() {
    memset(dis, -1, sizeof(dis));
    memset(gap, 0, sizeof(gap));
    gap[0] = 1;
    dis[ei] = 0;
    que.push(ei);
    while(!que.empty()) {
        int u = que.front(); que.pop();
        for (int i = pre[u]; i + 1; i = edge[i].nxt) {
            if(dis[edge[i].v] == -1 && edge[i ^ 1].w) {
                dis[edge[i].v] = dis[u] + 1;
                que.push(edge[i].v);
                gap[dis[edge[i].v]] ++;
            }
        }
    }
}

main函数

 bfs();
        while(dis[si] < n) {
            isFind = 0;
            aug = INF;
            SAP(si);
        }

猜你喜欢

转载自blog.csdn.net/henu_jizhideqingwa/article/details/81870019