网络流入门

贴一个网络流详解

gap优化之最大流(能解决几乎所有最大流问题)

简述一下:首先反向给节点标号,然后,正向跑,过程中用gap判断是否有断层,即达到优化的效果。

const int maxn = 1200;
const int maxe = 500000;
const int inf = 1<<30;

struct Edge
{
    int v,cap,next;
}edge[maxe];
int level[maxn];
int gap[maxn],pre[maxn],cur[maxn],head[maxn];

int NV,NE;

void add_edge(int u,int v,int cap)
{
    edge[NE].v=v;
    edge[NE].cap=cap;
    edge[NE].next=head[u];
    head[u]=NE++;

    edge[NE].v=u;
    edge[NE].cap=0;
    edge[NE].next=head[v];
    head[v]=NE++;
}

void bfs(int vt)
{
    memset(level,-1,sizeof(level));
    memset(gap,0,sizeof(gap));
    level[vt]=0;
    gap[level[vt]]++;
    queue<int>que;
    que.push(vt);
    while(!que.empty()) {
        int u=que.front();
        que.pop();
        for(int i=head[u]; i!=-1; i=edge[i].next) {
            int v=edge[i].v;
            if(level[v]!=-1)continue;
            level[v]=level[u]+1;
            gap[level[v]]++;
            que.push(v);

        }
    }
}

int SAP(int vs,int vt)
{
    bfs(vt);
    memset(pre,-1,sizeof(pre));
    memcpy(cur,head,sizeof(head));
    int u=pre[vs]=vs,flow=0,aug=inf;
    gap[0]=NV;
    while(level[vs]<NV) {
        bool flag=false;
        for(int &i=cur[u]; i!=-1; i=edge[i].next) {//这里注意int &i用了一个引用,i的变化等价于cur[i]的变化,注意这个细节否则下面会很疑惑。
            int v=edge[i].v;
            if(edge[i].cap&&level[u]==level[v]+1) {
                flag=true;
                pre[v]=u;
                u=v;
                //  aug=(aug==-1?edge[i].cap:min(aug,edge[i].cap));
                aug=min(aug,edge[i].cap);
                if(v==vt) {
                    flow+=aug;
                    for(u=pre[v]; v!=vs; v=u,u=pre[u]) {
                        edge[cur[u]].cap-=aug;
                        edge[cur[u]^1].cap+=aug;
                    }
                    //     aug=-1;
                    aug=inf;
                }
                break;//注意每找到下一步之后便跳出循环
            }
        }
        if(flag)continue;
        int minlevel=NV;
        for(int i=head[u]; i!=-1; i=edge[i].next) {
            int v=edge[i].v;
            if(edge[i].cap&&level[v]<minlevel) {
                minlevel=level[v];
                cur[u]=i;
            }
        }
        if(--gap[level[u]]==0)break;//gap[优化重点]
        level[u]=minlevel+1;
        gap[level[u]]++;
        u=pre[u];//注意由于level[u]的改变,pre[u]的后继就不一定是u了,所以要返回pre[u]重新找增广路。
    }
    return flow;
}

EK:暴力bfs

#include <queue>
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
const int MAXN = 300;
const int MAX_INT = ((1 << 31) - 1);

int n;                                      // 图中点的数目
int pre[MAXN];                              // 从 s - t 中的一个可行流中, 节点 i 的前序节点为 Pre[i];
bool vis[MAXN];                             // 标记一个点是否被访问过
int mp[MAXN][MAXN];                         // 记录图信息

bool bfs(int s, int t){
    queue <int> que;
    memset(vis, 0, sizeof(vis));
    memset(pre, -1, sizeof(pre));
    pre[s] = s;
    vis[s] = true;
    que.push(s);
    while(!que.empty()){
        int u = que.front();
        que.pop();
        for(int i = 1; i <= n; i++){
            if(mp[u][i] && !vis[i]){
                pre[i] = u;
                vis[i] = true;
                if(i == t) return true;
                que.push(i);
            }
        }
    }
    return false;
}

int EK(int s, int t){
    int ans = 0;
    while(bfs(s, t)){
        int mi = MAX_INT;
        for(int i = t; i != s; i = pre[i]){
            mi = min(mi, mp[pre[i]][i]);
        }
        for(int i = t; i != s; i = pre[i]){
            mp[pre[i]][i] -= mi;
            mp[i][pre[i]] += mi;
        }
        ans += mi;
    }
    return ans;
}

最小费用最大流模板
与EK基本相同,只是把bfs换成spfa
理由:在EK中,我们每次找最短路增广,正确性显然

但是在最小费用最大流中,我们要考虑费用的问题,所以,最短路增广可能并非最优,因此每个节点可能访问一次后重新访问。

#include <queue>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int MAXN = 1010;
const int MAXM = 1000100;
const int MAXN_INT = (1 << 29);

struct Edge{
    int v, w, c, nxt;
};

struct Node{
    int id, v;
};

bool vis[MAXN];
Node pre[MAXN];
Edge edge[MAXN];
int n, m, ecnt, sumFlow;
int head[MAXN], dis[MAXN];

void init(){
    ecnt = 0;
    memset(edge, 0, sizeof(edge));
    memset(head, -1, sizeof(head));
}

void addEdge(int u, int v, int c, int w){
    edge[ecnt].v = v;
    edge[ecnt].w = w;
    edge[ecnt].c = c;
    edge[ecnt].nxt = head[u];
    head[u] = ecnt++;
}

bool SPFA(int s, int t, int n){
    queue <int> que;
    memset(vis, 0, sizeof(vis));
    fill(dis, dis + MAXN, MAXN_INT);
    vis[s] = true;
    dis[s] = 0;
    que.push(s);
    while(!que.empty()){
        int u =que.front();
        que.pop();
        vis[u] = false;
        for(int i = head[u]; i + 1; i = edge[i].nxt){
            int v = edge[i].v;
            if(edge[i].c && dis[v] > dis[u] + edge[i].c){
                dis[v] = dis[u] + edge[i].c;
                pre[v].v = u;
                pre[v].id = i;
                if(!vis[v]){
                    que.push(v);
                    vis[v] = true;
                }
            }
        }
    }
    if(dis[t] == MAXN_INT) return false;
    return true;
}

int MCMF(int s, int t, int n){
    int flow = 0;
    int minCost = 0;
    while(SPFA(s, t, n)){
        int minFlow = MAXN_INT + 1;
        for(int i = t; i != s; i = pre[i].v){
            minFlow = min(minFlow, edge[pre[i].id].w);
        }

        for(int i = t; i != s; i = pre[i].v){
            edge[pre[i].id].w -= minFlow;
            edge[pre[i].id ^ 1].w += minFlow;
        }
        minCost += dis[t] * minFlow;
    }
    sumFlow = flow;
    return minCost;
}

int main(){
    while(scanf("%d%d", &n, &m) != EOF){
        int u, v, c, w;
        for(int i = 0; i < m; i++){
            scanf("%d%d%d%d", &u, &v, &c, &w);
            addEdge(u, v, c, w);
            addEdge(v, u, -c, 0);
        }
        int ans = MCMF(1, n, n);
        printf("%d\n", ans);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_35160381/article/details/80302985