[最大流&费用流&上下界&最小流]-模板

网络流

(_{_{_{_{?}^{?}{?}_{?}^{?}}^{_{?}^{?}{?}_{?}^{?}}{_{?}^{?}{?}_{?}^{?}}_{_{?}^{?}{?}_{?}^{?}}^{_{?}^{?}{?}_{?}^{?}}}^{_{_{?}^{?}{?}_{?}^{?}}^{_{?}^{?}{?}_{?}^{?}}{_{?}^{?}{?}_{?}^{?}}_{_{?}^{?}{?}_{?}^{?}}^{_{?}^{?}{?}_{?}^{?}}}{_{_{?}^{?}{?}_{?}^{?}}^{_{?}^{?}{?}_{?}^{?}}{_{?}^{?}{?}_{?}^{?}}_{_{?}^{?}{?}_{?}^{?}}^{_{?}^{?}{?}_{?}^{?}}}_{_{_{?}^{?}{?}_{?}^{?}}^{_{?}^{?}{?}_{?}^{?}}{_{?}^{?}{?}_{?}^{?}}_{_{?}^{?}{?}_{?}^{?}}^{_{?}^{?}{?}_{?}^{?}}}^{_{_{?}^{?}{?}_{?}^{?}}^{_{?}^{?}{?}_{?}^{?}}{_{?}^{?}{?}_{?}^{?}}_{_{?}^{?}{?}_{?}^{?}}^{_{?}^{?}{?}_{?}^{?}}}}^{_{_{_{?}^{?}{?}_{?}^{?}}^{_{?}^{?}{?}_{?}^{?}}{_{?}^{?}{?}_{?}^{?}}_{_{?}^{?}{?}_{?}^{?}}^{_{?}^{?}{?}_{?}^{?}}}^{_{_{?}^{?}{?}_{?}^{?}}^{_{?}^{?}{?}_{?}^{?}}{_{?}^{?}{?}_{?}^{?}}_{_{?}^{?}{?}_{?}^{?}}^{_{?}^{?}{?}_{?}^{?}}}{_{_{?}^{?}{?}_{?}^{?}}^{_{?}^{?}{?}_{?}^{?}}{_{?}^{?}{?}_{?}^{?}}_{_{?}^{?}{?}_{?}^{?}}^{_{?}^{?}{?}_{?}^{?}}}_{_{_{?}^{?}{?}_{?}^{?}}^{_{?}^{?}{?}_{?}^{?}}{_{?}^{?}{?}_{?}^{?}}_{_{?}^{?}{?}_{?}^{?}}^{_{?}^{?}{?}_{?}^{?}}}^{_{_{?}^{?}{?}_{?}^{?}}^{_{?}^{?}{?}_{?}^{?}}{_{?}^{?}{?}_{?}^{?}}_{_{?}^{?}{?}_{?}^{?}}^{_{?}^{?}{?}_{?}^{?}}}}{_{_{_{?}^{?}{?}_{?}^{?}}^{_{?}^{?}{?}_{?}^{?}}{_{?}^{?}{?}_{?}^{?}}_{_{?}^{?}{?}_{?}^{?}}^{_{?}^{?}{?}_{?}^{?}}}^{_{_{?}^{?}{?}_{?}^{?}}^{_{?}^{?}{?}_{?}^{?}}{_{?}^{?}{?}_{?}^{?}}_{_{?}^{?}{?}_{?}^{?}}^{_{?}^{?}{?}_{?}^{?}}}{_{_{?}^{?}{?}_{?}^{?}}^{_{?}^{?}{?}_{?}^{?}}{_{?}^{?}{?}_{?}^{?}}_{_{?}^{?}{?}_{?}^{?}}^{_{?}^{?}{?}_{?}^{?}}}_{_{_{?}^{?}{?}_{?}^{?}}^{_{?}^{?}{?}_{?}^{?}}{_{?}^{?}{?}_{?}^{?}}_{_{?}^{?}{?}_{?}^{?}}^{_{?}^{?}{?}_{?}^{?}}}^{_{_{?}^{?}{?}_{?}^{?}}^{_{?}^{?}{?}_{?}^{?}}{_{?}^{?}{?}_{?}^{?}}_{_{?}^{?}{?}_{?}^{?}}^{_{?}^{?}{?}_{?}^{?}}}}_{_{_{_{?}^{?}{?}_{?}^{?}}^{_{?}^{?}{?}_{?}^{?}}{_{?}^{?}{?}_{?}^{?}}_{_{?}^{?}{?}_{?}^{?}}^{_{?}^{?}{?}_{?}^{?}}}^{_{_{?}^{?}{?}_{?}^{?}}^{_{?}^{?}{?}_{?}^{?}}{_{?}^{?}{?}_{?}^{?}}_{_{?}^{?}{?}_{?}^{?}}^{_{?}^{?}{?}_{?}^{?}}}{_{_{?}^{?}{?}_{?}^{?}}^{_{?}^{?}{?}_{?}^{?}}{_{?}^{?}{?}_{?}^{?}}_{_{?}^{?}{?}_{?}^{?}}^{_{?}^{?}{?}_{?}^{?}}}_{_{_{?}^{?}{?}_{?}^{?}}^{_{?}^{?}{?}_{?}^{?}}{_{?}^{?}{?}_{?}^{?}}_{_{?}^{?}{?}_{?}^{?}}^{_{?}^{?}{?}_{?}^{?}}}^{_{_{?}^{?}{?}_{?}^{?}}^{_{?}^{?}{?}_{?}^{?}}{_{?}^{?}{?}_{?}^{?}}_{_{?}^{?}{?}_{?}^{?}}^{_{?}^{?}{?}_{?}^{?}}}}^{_{_{_{?}^{?}{?}_{?}^{?}}^{_{?}^{?}{?}_{?}^{?}}{_{?}^{?}{?}_{?}^{?}}_{_{?}^{?}{?}_{?}^{?}}^{_{?}^{?}{?}_{?}^{?}}}^{_{_{?}^{?}{?}_{?}^{?}}^{_{?}^{?}{?}_{?}^{?}}{_{?}^{?}{?}_{?}^{?}}_{_{?}^{?}{?}_{?}^{?}}^{_{?}^{?}{?}_{?}^{?}}}{_{_{?}^{?}{?}_{?}^{?}}^{_{?}^{?}{?}_{?}^{?}}{_{?}^{?}{?}_{?}^{?}}_{_{?}^{?}{?}_{?}^{?}}^{_{?}^{?}{?}_{?}^{?}}}_{_{_{?}^{?}{?}_{?}^{?}}^{_{?}^{?}{?}_{?}^{?}}{_{?}^{?}{?}_{?}^{?}}_{_{?}^{?}{?}_{?}^{?}}^{_{?}^{?}{?}_{?}^{?}}}^{_{_{?}^{?}{?}_{?}^{?}}^{_{?}^{?}{?}_{?}^{?}}{_{?}^{?}{?}_{?}^{?}}_{_{?}^{?}{?}_{?}^{?}}^{_{?}^{?}{?}_{?}^{?}}}})

1.最大流

/***********************************
dinic最大流思路:
    不断图分层,保证dfs过程中,不会出现环等现象
    在一次分层中,用dfs一次性搜寻多条增广路。
    就是向下搜,搜到t为止,然后返回流量。
    当前节点最大流量为的下属分支所有流量的返回最大流量。
    下属分支返回的流量为min(通往该分支边的最大流量,分支的下属总流量)
    当dfs一次后,一定会有某些边被榨干,改变分层图。
    再次分层图,重复上述过程。
    分层图用bfs实现。如果bfs不到终点,则结束算法。
************************************/
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<queue>
using namespace std;
typedef long long ll;
typedef long long ii;
const ll maxn = 205;
const ll maxm = 2e3+40;
const ll INF = ll(1)<<50;
struct e{
	ll next,to,flow;
}edge[maxm*2];
int head[maxn],tot=0;
void add(ll u,ll v,ll flow){
	edge[tot].to=v;edge[tot].next=head[u];
	edge[tot].flow=flow;head[u]=tot++;
	edge[tot].to=u;edge[tot].next=head[v];
	edge[tot].flow=0;head[v]=tot++;
}
ll n,m,s,t;

ll dis[maxn];
queue<ll> q;
ll cur[maxn];

ll dfs(ll u,ll lim){
	//cout<<"dfs"<<endl;
//	/system("pause");
	if(u==t)return lim;
	ll flow=0;
	for(ll i=cur[u];~i;i=edge[i].next){
		cur[u]=i;
		//最优弧优化。
		//因为不是树,是网络图,所以回溯后,可能从其他分支再经过当前点。
		//而当前点的之前的边流量一定已经被榨干了。
		//所以不用考虑当前边的前边的边
		ll v= edge[i].to;
		if(dis[v]!=dis[u]+1||edge[i].flow==0){
            continue;
		}
		ll f=dfs(v,min(edge[i].flow,lim));
		flow+=f;lim-=f;
		edge[i].flow-=f;edge[i^1].flow+=f;
		if(lim==0)break;
	}
	return flow;
}

ll bfs(){
	while(!q.empty())q.pop();
	for(ll i=1;i<=n;i++){
		dis[i]=INF;
		cur[i]=head[i];
	}
	dis[s]=0;
	q.push(s);
	while(!q.empty()){
		ll u=q.front();q.pop();
		for(ll i=head[u];~i;i=edge[i].next){
			ll v= edge[i].to;
            //cout<<i<<" "<<v<<endl;
			if(edge[i].flow==0||dis[v]!=INF)continue;
			dis[v]=dis[u]+1;
			q.push(v);
		}
	}
	if(dis[t]==INF)return 0;
	else return 1;
}

void init(){
	for(ll i=0;i<=n;i++){
		head[i]=-1;
	}
}
int main(){
   // cout<<INF+1<<endl;
	ll x;
	scanf("%lld%lld%lld",&n,&m,&x);
	init();
	s=1;t=n;
	ll a,b,c;
	for(int i=0;i<m;i++){
		scanf("%lld%lld%lld",&a,&b,&c);
		add(a,b,c);
	}
	/*
	for(int i=0;i<tot;i++){
        cout<<i<<":"<<edge[i].to<<" "<<edge[i].next<<" "<<edge[i].flow<<endl;
	}*/
	ll maxflow=0;
	while(bfs()){
        maxflow+=dfs(s,INF);
        //cout<<maxflow<<endl;
	}
    if(maxflow==0)puts("Orz Ni Jinan Saint Cow!");
	else printf("%lld %lld",maxflow,(x+maxflow-1)/maxflow);
	return 0;
}

2.最小费用流

/******************************************************
整体思路:
    首先在图中找到一条流不为0的最少费用路
    可用dij或者spfa,dij的话要给所有的加势;
    在找最短路的过程中,cost看做路径长度。
    用pre[i]来存储前一条边是哪条边,因为最后
    要处理所有途径边的流量,和计算cost;
    cost为dis[t]*incf[t];
    dis[t]为路径上累计长度,即累计单位流量花费。
    然后重复调用上述过程,直到找不到一条最短路径。
*******************************************************/






#include<iostream>
#include<queue>
#include<cstdlib>
#include<cstdio>
using namespace std;
int n,m,s,t;
int maxflow,mincost;
const int maxn=5e3+50,maxm=5e4+50;

struct e{
    int next,to,flow,cost;
}edge[maxm*2];
int head[maxn],tot=0;
void add(int u,int v,int flow,int cost){
    edge[tot].flow=flow;edge[tot].cost=cost;
    edge[tot].next=head[u];edge[tot].to=v;
    head[u]=tot++;
    edge[tot].flow=0;edge[tot].cost=-cost;
    edge[tot].next=head[v];edge[tot].to=u;
    head[v]=tot++;
}

int dis[maxn];
int incf[maxn];
bool inq[maxn];
int pre[maxn];
bool spfa(){
    queue<int> q;
    for(int i=0;i<=n;i++){
        dis[i]=0x7fffffff;
        inq[i]=false;
    }
    q.push(s);
    inq[s]=1;dis[s]=0;
    incf[s]=0x7fffffff;
    while(!q.empty()){
        int u=q.front();q.pop();
        inq[u]=0;
        for(int i=head[u];~i;i=edge[i].next){
            if(edge[i].flow==0)continue;
            int v=edge[i].to;
            if(dis[v]>dis[u]+edge[i].cost){
                dis[v]=dis[u]+edge[i].cost;
                incf[v]=min(edge[i].flow,incf[u]);
                pre[v]=i;
                //存前驱的边是因为最后要通过前驱来快速会找到边从而操作cost和flow
                //存点的遍历所有的边。
                //前驱点的寻找可以用反向边的to来寻找。
                if(!inq[v]){
                    q.push(v);inq[v]=true;
                }
            }
        }
    }
    if(dis[t]==0x7fffffff)return 0;
    else return 1;
}
void MCMF(){
    while(spfa()){
        int x=t;
        maxflow += incf[t];
        mincost += dis[t]*incf[t];
        int i;
        while(x!=s){
            i=pre[x];
            edge[i].flow -= incf[t];
            edge[i^1].flow += incf[t];
            x = edge[i^1].to;
        }
    }
}
void init(){
    for(int i=0;i<=n;i++){
        head[i]=-1;
    }
}

int main(){
    maxflow=mincost=0;
    scanf("%d%d%d%d",&n,&m,&s,&t);
    init();
    int u,v,cost,flow;
    for(int i=0;i<m;i++){
        scanf("%d%d%d%d",&u,&v,&flow,&cost);
        add(u,v,flow,cost);
    }
    MCMF();
    printf("%d %d
",maxflow,mincost);
    return 0;
}

3.无源汇上下界可行流

/*******************
无源汇上下界可行流:
    首先将图中的下界流跑满
    建图的边改为上界减下界
    那么会出现部分点的流不平衡
    我们给这些点分别连一个起点和终点。
    给缺少出流的连上起点,缺少入流的连上终点。
    边的流上界为所缺少或多余的流
    这样在跑满最大流后,
    如果连接终点和起点的流跑满之后,
    把终点和起点去掉,那么终点和起点连接的节点
    会分别多出/少了连边对应边的满流。
    因为前面是按照缺少/多余建图
    所以缺少/多余各自补平。

    做法:
        建图,边为上界减下界,存下下界
        建源点汇点,连统计入度出度的边
        跑最大流
        输出边时输出下界流+最大流的流
********************/
#include <iostream>
#include <cstdio>
#include <queue>
#include <cmath>
#include <cstdlib>
#include <algorithm>
using namespace std;
const int maxn = 219;
const int maxm = 10250;

struct e {
    int next, to, flow;
} edge[maxm * 2];
int head[maxn], tot = 0;
void add(int u, int v, int flow) {
    edge[tot].next = head[u];
    edge[tot].to = v;
    edge[tot].flow = flow;
    head[u] = tot++;

    edge[tot].next = head[v];
    edge[tot].to = u;
    edge[tot].flow = 0;
    head[v] = tot++;
}

int s, t, n, m;

int cur[maxn + 2];
int dis[maxn + 2];
int dfs(int u, int lim) {
    if (u == t || lim == 0)
        return lim;
    int flow = 0;
    for (int i = cur[u]; ~i; i = edge[i].next) {
        // cout<<cur[u]<<endl;
        //  system("pause");

        cur[u] = i;
        int v = edge[i].to;
        if (dis[v] != dis[u] + 1)
            continue;
        int f = dfs(v, min(lim, edge[i].flow));
        flow += f;
        lim -= f;
        edge[i].flow -= f;
        edge[i ^ 1].flow += f;
        if (lim == flow)
            break;
    }
    return flow;
}

bool bfs() {
    for (int i = 0; i <= n + 5; i++) {
        dis[i] = 0x7fffffff;
        cur[i] = head[i];
    }
    dis[s] = 0;
    queue<int> q;
    q.push(s);
    while (!q.empty()) {
        int u = q.front();
        q.pop();
        for (int i = head[u]; ~i; i = edge[i].next) {
            int v = edge[i].to;
            if (dis[v] != 0x7fffffff || edge[i].flow == 0)
                continue;
            dis[v] = dis[u] + 1;
            q.push(v);
        }
    }
    if (dis[t] == 0x7fffffff)
        return 0;
    else
        return 1;
}

int maxflow = 0;
bool dinic() {
    while (bfs()) {
        maxflow += dfs(s, 0x7fffffff);
    }
    for (int i = head[s]; ~i; i = edge[i].next) {
        if (edge[i].flow != 0)
            return 0;
    }
    return 1;
}

int level[maxn];
int addd[maxm];
void init() {
    for (int i = 0; i <= n + 2; i++) {
        level[i] = 0;
        head[i] = -1;
    }
    for (int i = 0; i < m; i++) {
        addd[i] = 0;
    }
}

int main() {
    freopen("2.in","r",stdin);
    scanf("%d%d", &n, &m);
    init();
    int a, b, l, u;
    for (int i = 0; i < m; i++) {
        scanf("%d%d%d%d", &a, &b, &l, &u);
        add(a, b, u - l);
        level[b] += l;
        level[a] -= l;
        addd[i] += l;
    }
    s = n + 1, t = n + 2;
    int first = 0;
    for (int i = 1; i <= n; i++) {
        if (level[i] > 0) {
            add(s, i, level[i]);
        }
        if (level[i] < 0) {
            first += level[i];
            add(i, t, -level[i]);
        }
    }

    if (dinic()) {
        puts("YES");
        for (int i = 0; i < m; i++) {
            cout << edge[i * 2 + 1].flow + addd[i] << endl;
        }
    } else
        puts("NO");

    return 0;
}

4.有源汇上下界最小流

/*******************
无源汇上下界可行流:
    首先将图中的下界流跑满
    建图的边改为上界减下界
    那么会出现部分点的流不平衡
    我们给这些点分别连一个起点和终点。
    给缺少出流的连上起点,缺少入流的连上终点。
    边的流上界为所缺少或多余的流
    这样在跑满最大流后,
    如果连接终点和起点的流跑满之后,
    把终点和起点去掉,那么终点和起点连接的节点
    会分别多出/少了连边对应边的满流。
    因为前面是按照缺少/多余建图
    所以缺少/多余各自补平。

    做法:
        建图,边为上界减下界,存下下界
        建源点汇点,连统计入度出度的边
        跑最大流
        输出边时输出下界流+最大流的流
********************/
#include <iostream>
#include <cstdio>
#include <queue>
#include <cmath>
#include <cstdlib>
#include <algorithm>
using namespace std;
const int maxn = 219;
const int maxm = 10250;

struct e {
    int next, to, flow;
} edge[maxm * 2];
int head[maxn], tot = 0;
void add(int u, int v, int flow) {
    edge[tot].next = head[u];
    edge[tot].to = v;
    edge[tot].flow = flow;
    head[u] = tot++;

    edge[tot].next = head[v];
    edge[tot].to = u;
    edge[tot].flow = 0;
    head[v] = tot++;
}

int s, t, n, m;

int cur[maxn + 2];
int dis[maxn + 2];
int dfs(int u, int lim) {
    if (u == t || lim == 0)
        return lim;
    int flow = 0;
    for (int i = cur[u]; ~i; i = edge[i].next) {
        // cout<<cur[u]<<endl;
        //  system("pause");

        cur[u] = i;
        int v = edge[i].to;
        if (dis[v] != dis[u] + 1)
            continue;
        int f = dfs(v, min(lim, edge[i].flow));
        flow += f;
        lim -= f;
        edge[i].flow -= f;
        edge[i ^ 1].flow += f;
        if (lim == flow)
            break;
    }
    return flow;
}

bool bfs() {
    for (int i = 0; i <= n + 5; i++) {
        dis[i] = 0x7fffffff;
        cur[i] = head[i];
    }
    dis[s] = 0;
    queue<int> q;
    q.push(s);
    while (!q.empty()) {
        int u = q.front();
        q.pop();
        for (int i = head[u]; ~i; i = edge[i].next) {
            int v = edge[i].to;
            if (dis[v] != 0x7fffffff || edge[i].flow == 0)
                continue;
            dis[v] = dis[u] + 1;
            q.push(v);
        }
    }
    if (dis[t] == 0x7fffffff)
        return 0;
    else
        return 1;
}

int maxflow = 0;
bool dinic() {
    while (bfs()) {
        maxflow += dfs(s, 0x7fffffff);
    }
    for (int i = head[s]; ~i; i = edge[i].next) {
        if (edge[i].flow != 0)
            return 0;
    }
    return 1;
}

int level[maxn];
int addd[maxm];
void init() {
    for (int i = 0; i <= n + 2; i++) {
        level[i] = 0;
        head[i] = -1;
    }
    for (int i = 0; i < m; i++) {
        addd[i] = 0;
    }
}

int main() {
    freopen("2.in","r",stdin);
    scanf("%d%d", &n, &m);
    init();
    int a, b, l, u;
    for (int i = 0; i < m; i++) {
        scanf("%d%d%d%d", &a, &b, &l, &u);
        add(a, b, u - l);
        level[b] += l;
        level[a] -= l;
        addd[i] += l;
    }
    s = n + 1, t = n + 2;
    int first = 0;
    for (int i = 1; i <= n; i++) {
        if (level[i] > 0) {
            add(s, i, level[i]);
        }
        if (level[i] < 0) {
            first += level[i];
            add(i, t, -level[i]);
        }
    }

    if (dinic()) {
        puts("YES");
        for (int i = 0; i < m; i++) {
            cout << edge[i * 2 + 1].flow + addd[i] << endl;
        }
    } else
        puts("NO");

    return 0;
}

原文地址:https://www.cnblogs.com/greenpepper/p/13361886.html