1. 程式人生 > 其它 >最大流&最小割&費用流模版

最大流&最小割&費用流模版

好久都沒有搞部落格了。想認真寫又要準備文化課期末了。

ISAP

  • 流程: 原理就是dfs找增廣路
    最基礎的建反向邊以便反悔就不說了。
    但是記錄一個dep(dis)表示層數,一開始BFS(從t開始,dis[t]=0)處理最小層數,然後再搜尋增廣路增加限制條件:dis[u]=dis[v]+1,若這樣的v找完了,擴大一層u(即dis[u]++),可能會被回溯到前面的某條新路再次搜中。然後特判一下,如果dis[s]>=n即可結束,因為dis[t]永遠等於0,dis[s]最大為n-1。
    gap(cnt)標記很簡單:就是記錄每個dis[]值的個數,若某一中間dis[]值為0,則出現了斷層,理論上就搜不到增廣路了。
  • 程式碼:
#include<bits/stdc++.h>
using namespace std;
const int N=1e4+5;
const int inf=0x3f3f3f3f; 
int nxt[N],to[N],head[N],len[N],num=1;
void add_edge(int u,int v,int w) {nxt[++num]=head[u];to[num]=v;len[num]=w;head[u]=num;}
int dis[N],n,m,s,t,gap[N];
void bfs()
{
    memset(dis,-1,sizeof(dis));
    memset(gap,0,sizeof(gap));
    dis[t]=0,gap[0]=1;
    queue<int>q; 
    q.push(t);
    while(!q.empty()) {
        int u=q.front();
        q.pop();
        for(int i=head[u];i;i=nxt[i]) {
            int v=to[i];
            if(dis[v]!=-1)continue;
            q.push(v);
            dis[v]=dis[u]+1,gap[dis[v]]++;
        }
    }
    return;
}
long long maxflow;
long long dfs(int u,long long flow) {
    if(u==t) return flow;
    long long used=0;
    for(int i=head[u];i;i=nxt[i]) {
        int v=to[i];
        if(len[i]&&dis[v]+1==dis[u]) {
            int tmp=dfs(v,min(1ll*len[i],flow-used));
            if(tmp) {
                len[i]-=tmp;
                len[i^1]+=tmp;
                used+=tmp;
            }
            if(used==flow)return used;
        }
    }
    --gap[dis[u]];
    if(gap[dis[u]]==0)dis[s]=n;
    dis[u]++,gap[dis[u]]++;
    return used; 
}
long long ISAP()
{
    maxflow=0;
    for(bfs();dis[s]<n;maxflow+=dfs(s,inf));
    return maxflow;
}
int main() {
    scanf("%d%d%d%d",&n,&m,&s,&t);
    int u,v,w;
    for(int i=1;i<=m;i++) {
		scanf("%d%d%d",&u,&v,&w);
		add_edge(u,v,w);
		add_edge(v,u,0);
	}
    printf("%lld\n",ISAP());
    return 0;
}

費用流演算法

  • 流程:
    首先變化在於,找最大流的基礎上滿足最小(大)費用。其次,每條邊上多了費用一變數,因此我們在存反邊時邊的費用為負的原費用
    然後我們把EK_BFS找增廣路改為找最短(長)路即可。注意記錄前驅,最後結束時更新邊資訊。
  • 程式碼:
#include<stdio.h>
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5;
int mxflow=0,mnval=0,dis[N],head[N],n,m,s,t,to[N],nxt[N],len[N],val[N],ecnt=1,flow[N],pre[N],inf=0x3f3f3f3f;
void add_edge(int u,int v,int z,int w) {
	nxt[++ecnt]=head[u];to[ecnt]=v;len[ecnt]=z;val[ecnt]=w;head[u]=ecnt;
	nxt[++ecnt]=head[v];to[ecnt]=u;len[ecnt]=0;val[ecnt]=-w;head[v]=ecnt;
}
bool In_q[N];
queue<int> Q;
void init() {
	for(int i=0;i<=n;i++)dis[i]=flow[i]=inf,In_q[i]=0,pre[i]=0;
}
bool spfa() {
	init();
	Q.push(s); dis[s]=0,In_q[s]=1;
	while(!Q.empty()) {
		int u=Q.front(); Q.pop(); In_q[s]=0;
		for(int i=head[u];i;i=nxt[i]) {
			int v=to[i];
			if(len[i]&&dis[v]>dis[u]+val[i]) {
				dis[v]=dis[u]+val[i];
				flow[v]=min(flow[u],len[i]);
				pre[v]=i^1;	//記錄終點往前的邊的編號 
				Q.push(v),In_q[v]=1;
			}
		}
	}
	if(dis[t]==0x3f3f3f3f) return false;
	return true;
}
void mn_valflow() {
	while(spfa()) {
		mxflow+=flow[t];
		mnval+=flow[t]*dis[t];
		int k=t;
		while(pre[k]) {
			len[pre[k]]+=flow[t],len[pre[k]^1]-=flow[t];
			k=to[pre[k]];
		}
	}
}
int main() {
	scanf("%d%d%d%d",&n,&m,&s,&t);
	for(int i=1;i<=m;i++) {
		int u,v,z,w;
		scanf("%d%d%d%d",&u,&v,&z,&w);
		add_edge(u,v,z,w);
	}
	mn_valflow();
	printf("%d %d",mxflow,mnval);
	return 0;
}