好久都没有搞博客了。想认真写又要准备文化课期末了。

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;
#define int long long
const int inf = 0x3f3f3f3f;
const int N = 1e6 + 5;
int head[N], cur[N], to[N], nxt[N], S, T, dis[N], gap[N], ecnt = 1, len[N], n, m;
void add_edge(int u, int v, int w) {
	nxt[++ecnt] = head[u]; to[ecnt] = v; len[ecnt] = w; cur[u] = head[u] = ecnt;
	nxt[++ecnt] = head[v]; to[ecnt] = u; len[ecnt] = 0; cur[v] = head[v] = ecnt;
}

void BFS() {
	memset(dis, -1, sizeof(dis)); 
	memset(gap, 0, sizeof(gap));
	queue<int> Q;
	Q.push(T);
	gap[dis[T] = 0]++;
	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;
			gap[dis[v] = dis[u] + 1]++;
			Q.push(v); 
		}
	}
}

int dfs(int u, int flow) {
	if(u == T) return flow;
	int used = 0;
	for(int i = cur[u]; i; i = nxt[i]) {
		int v = to[i];
		if(len[i] && dis[v] + 1 == dis[u]) {
			int tmp = dfs(v, min(flow - used, len[i]));	
			if(tmp) {
				len[i] -= tmp; len[i ^ 1] += tmp;
				used += tmp;
				if(used == flow) return flow;
			}
		}
		cur[u] = i;		//下次遍历到 u时, dis不变而前面的已经考虑过了。 
	}
	if(!--gap[dis[u]++]) {dis[S] = n;}
	gap[dis[u]]++;
	cur[u] = head[u];
	return used;
}

int ISAP() {
	int mxflow = 0;
	for(BFS(); dis[S] < n; mxflow += dfs(S, inf));
	return mxflow;
}

signed main() {
	scanf("%d%d%d%d", &n, &m, &S, &T);
	for(int i = 1; i <= m; i++) {
		int u, v, w; scanf("%d%d%d", &u, &v, &w);
		add_edge(u, v, w);
	}
	int ans = ISAP();
	printf("%lld", ans);
	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;
}