程序竞赛中的网络流

前言

      网络流是ACM图论中比较重要且难懂的一块。网络流的最经典应用就是最大流,求解网络流的基本思想就是每次寻找增广路。

相关定义

      源点:有n个点,有m条有向边,有一个点很特殊,只出不进,叫做源点

      汇点:另一个点也很特殊,只进不出,叫做汇点

      容量和流量:每条有向边上有两个量,容量和流量,从i到j的容量通常用c[i,j]表示,流量则通常是f[i,j].

      最大流:把源点比作工厂的话,问题就是求从工厂最大可以发出多少货物,是不至于超过道路的容量限制,也就是,最大流。

      残量(残余容量):每条边中容量与流量的差

      反相边:若从点u到v的边容量为c,这条边上有流量f流过(称为正向边),则相当于从v到u有一条容量为0的边,其流量为-f,这条边是反向边。(作用:在有更优决策时,撤销已选的边) 

      残量网络:计算图中每条边的残量得到的网络,称为残量网络,并称残量网络上的s-t路径为增广路。 

      增广: 残量网络中任何一条从s到t的有向道路都对应一条原图中增广路——只要求出该道路中所有残量的最小值d,把对应的所有边上的流量增加d即可,这个过程称为增广。显然,只要残量网络中存在增广路,流量就可以增大。可以证明它的逆命题也成立:如果残量网络中不存在增广路,则当前流就是最大流。这就是著名的增广路定义。

Dinic最大流

/*==================================================*\ 
 | Dinic最大流 O(V^2 * E) 
 | INIT: ne=2; head[]置为0; addedge()加入所有弧; 
 | CALL: flow(n, s, t); 
\*==================================================*/ 
#define typec int                   // type of cost 
const typec inf = 0x3f3f3f3f;       // max of cost 
struct edge {
    int x, y, nxt;
    typec c;
} bf[E];
int ne, head[N], cur[N], ps[N], dep[N];

void addedge(int x, int y,
             typec c) { // add an arc(x -> y, c); vertex: 0 ~ n-1;  
    bf[ne].x = x; bf[ne].y = y; bf[ne].c = c;
    bf[ne].nxt = head[x];
    head[x] = ne++;
    bf[ne].x = y; bf[ne].y = x; bf[ne].c = 0;
    bf[ne].nxt = head[y];
    head[y] = ne++;
}

typec flow(int n, int s, int t) {
    typec tr, res = 0;
    int i, j, k, f, r, top;
    while (1) {
        memset(dep, -1, n * sizeof(int));
        for (f = dep[ps[0] = s] = 0, r = 1; f != r;)
            for (i = ps[f++], j = head[i]; j; j = bf[j].nxt) {
                if (bf[j].c && -1 == dep[k = bf[j].y]) {
                    dep[k] = dep[i] + 1;
                    ps[r++] = k;
                    if (k == t) {
                        f = r;
                        break;
                    }
                }
            }
        if (-1 == dep[t]) break;
        memcpy(cur, head, n * sizeof(int));
        for (i = s, top = 0;;) {
            if (i == t) {
                for (k = 0, tr = inf; k < top; ++k)
                    if (bf[ps[k]].c < tr)
                        tr = bf[ps[f = k]].c;
                for (k = 0; k < top; ++k) 
                    bf[ps[k]].c -= tr, bf[ps[k] ^ 1].c += tr;
                res += tr;
                i = bf[ps[top = f]].x;
            }
            for (j = cur[i]; cur[i]; j = cur[i] = bf[cur[i]].nxt) 
                if (bf[j].c && dep[i] + 1 == dep[bf[j].y]) 
                    break;
            if (cur[i]) {
                ps[top++] = cur[i];
                i = bf[cur[i]].y;
            } else {
                if (0 == top) break;
                dep[i] = -1;
                i = bf[ps[--top]].x;
            }
        }
    }
    return res;
} 

最小费用最大流(SPFA)

int V;
struct edge{
	int to,cap,cost,rev;
	edge(int t,int c,int co,int r):to(t),cap(c),cost(co),rev(r){}
}; 
int dist[Max_v];
bool used[Max_v];
int prv[Max_v],pre[Max_v]; //最短路的前驱节点和对应的边
vector<edge>G[Max_v]; 

void add_edge(int from,int to,int cap,int cost){
    G[from].push_back(edge(to,cap,cost,G[to].size()));
    G[to].push_back(edge(from,0,-cost,G[from].size()-1));
}

ll min_cost_flow(int s,int t,int f){
   ll ans=0;
    while(f>0){
        //用spfa寻找最短(费用)路
        memset(dist,0x3f,sizeof(dist));
        memset(used,0,sizeof(used));
        queue<int>que;
        que.push(s);
        dist[s]=0;used[s]=1;
        while(!que.empty()){
            int u=que.front();que.pop();
            used[u]=0;
            for(int i=0;i<G[u].size();i++){
                edge &e=G[u][i];
                if(e.cap>0&&dist[e.to]>dist[u]+e.cost){
                    dist[e.to]=dist[u]+e.cost;
                    prv[e.to]=u;pre[e.to]=i;
                    if(!used[e.to]){
                        que.push(e.to);
                        used[e.to]=1;
                    }
                }
            }
        }
        if(dist[t]==inf)//找不到增广路了
	        return -1; 
        //沿s到t的最短路尽量增广
        int d=f;
        for(int v=t;v!=s;v=prv[v]){
            d=min(d,G[prv[v]][pre[v]].cap);
        }
        f-=d;
        ans+=d*dist[t];
        for(int v=t;v!=s;v=prv[v]){
            edge &e=G[prv[v]][pre[v]];
            e.cap-=d;
            G[v][e.rev].cap+=d;
        }
    }
    return ans;
}

ISAP

int source;         // 源点
int sink;           // 汇点
int p[max_nodes];   // 可增广路上的上一条弧的编号
int num[max_nodes]; // 和 t 的最短距离等于 i 的节点数量
int cur[max_nodes]; // 当前弧下标
int d[max_nodes];   // 残量网络中节点 i 到汇点 t 的最短距离
bool visited[max_nodes];

// 预处理, 反向 BFS 构造 d 数组
bool bfs() {
    memset(visited, 0, sizeof(visited));
    queue<int> Q;
    Q.push(sink);
    visited[sink] = 1;
    d[sink] = 0;
    while (!Q.empty()) {
        int u = Q.front();
        Q.pop();
        for (iterator_t ix = G[u].begin(); ix != G[u].end(); ++ix) {
            Edge &e = edges[(*ix)^1];
            if (!visited[e.from] && e.capacity > e.flow) {
                visited[e.from] = true;
                d[e.from] = d[u] + 1;
                Q.push(e.from);
            }
        }
    }
    return visited[source];
}

// 增广
int augment() {
    int u = sink, df = __inf;
    // 从汇点到源点通过 p 追踪增广路径, df 为一路上最小的残量
    while (u != source) {
        Edge &e = edges[p[u]];
        df = min(df, e.capacity - e.flow);
        u = edges[p[u]].from;
    }
    u = sink;
    // 从汇点到源点更新流量
    while (u != source) {
        edges[p[u]].flow += df;
        edges[p[u]^1].flow -= df;
        u = edges[p[u]].from;
    }
    return df;
}

int max_flow() {
    int flow = 0;
    bfs();
    memset(num, 0, sizeof(num));
    for (int i = 0; i < num_nodes; i++) num[d[i]]++;
    int u = source;
    memset(cur, 0, sizeof(cur));
    while (d[source] < num_nodes) {
        if (u == sink) {
            flow += augment();
            u = source;
        }
        bool advanced = false;
        for (int i = cur[u]; i < G[u].size(); i++) { 
            Edge& e = edges[G[u][i]];
            if (e.capacity > e.flow && d[u] == d[e.to] + 1) {
                advanced = true;
                p[e.to] = G[u][i];
                cur[u] = i;
                u = e.to;
                break;
            }
        }
        if (!advanced) { // retreat
            int m = num_nodes - 1;
            for (iterator_t ix = G[u].begin(); ix != G[u].end(); ++ix)
                if (edges[*ix].capacity > edges[*ix].flow)
                    m = min(m, d[edges[*ix].to]);
            if (--num[d[u]] == 0) break; // gap 优化
            num[d[u] = m+1]++;
            cur[u] = 0;
            if (u != source)
                u = edges[p[u]].from;
        }
    }
    return flow;
}

代码多源于网络

猜你喜欢

转载自blog.csdn.net/qq_42024195/article/details/88927695