一句话网络流(EK,Dinic,最小费最大流,(最大/最小)可行流,最小费用可行流)

EK算法求最大流:
BFS找增广路增广直到无法增广
复杂度:O(V*E^2)
核心代码(邻接表存图):(一开始学最大流的时候写的代码,非常丑)

int pre[maxn];
int vis[maxn]={0};
bool bfs(){//bfs求路径
	for(int i = 0;i <= n+1;++i) vis[i] = 0;
	queue q;q.push(0);vis[0] = 1;
	while(q.size()){
		int u = q.front();q.pop();
		for(int i = head[u];i!=-1;i = e[i].nxt){
			int v = e[i].v;
			if(vis[v]) continue;
			if(e[i].f == 0) continue;
			vis[v] = 1;
			pre[v] = i;
			q.push(v);
		}
	}
	return vis[n+1];
}

while(bfs()){//更新正向边和反向边的流量
		ll d = inf;
		int u,v;
		v = n+1;
		while(v!=0){
			u = e[pre[v]].u;
			d = min(d,e[pre[v]].f);
			v = u;
		}
		v = n+1;
		while(v != 0){
			u = e[pre[v]].u;
			if(pre[v]&1) mp[u][v]-=d;
			else mp[u][v]+=d;
			e[pre[v]].f -= d;
			e[pre[v]^1].f += d;//给反向边加上回流 
			v = u;
		}
		ans += d;
	}

Dinic算法求最大流:
先用BFS求图的层次,然后DFS根据BFS求得的深度增广(只走深度比当前结点大1的点)
复杂度:O(V^2 * E)
核心代码:

int dep[maxn];
int q[maxn*2];
int tot,tail;
bool bfs(){
	memset(dep,-1,(ex+1)<<2);
	dep[st] = 1;
	q[tot = 0] = st,tail = 1;
	while(tot < tail){
		int u = q[tot++];
		if(u == ed) break;
		for(int i = head[u];~i;i = e[i].nxt){
			int v = e[i].v;
			if(dep[v]!=-1 || !e[i].f) continue;
			dep[v] = dep[u] + 1;
			q[tail++] = v;
		}
	}
	return dep[ed]!=-1;
}
int cur[maxn];
ll dfs(int u,ll flow){
	ll res = flow;
	if(u == ed) return flow;
	for(int &i = cur[u];~i;i = e[i].nxt){//弧优化
		int v = e[i].v;
		if(dep[v]!=dep[u] + 1 || !e[i].f) continue;
		int d = dfs(v,min(res,e[i].f));
		e[i].f -= d;
		e[i^1].f += d;
		res -= d;
		if(res == 0) break; 
	}
	if(flow == res) dep[u] = -1;//防止重新搜索 
	return flow - res;
}
int dinic(){
	ll ans = 0;
	int d;
	while(bfs()){
		for(int i = 0;i <= ex;++i) cur[i] = head[i];
		while(d = dfs(st,inf)) ans += d;
	}
	return ans;
}

最小费用流:
每次增广用spfa算法求得最短增广路,直到满流
复杂度分析:O(spfa算法复杂度*E)并不严谨
核心代码:

int pre[maxn];
int flow[maxn];
int inq[maxn];
int dis[maxn*maxn];
int spfa(){//获取最短增广路
	for(int i = 0;i <= ed;++i) pre[i] = -1,dis[i] = inf,inq[i] = 0;
	queue q;
	q.push(st);
	inq[st] = 1;
	dis[st] = 0;
	flow[st] = inf;
	while(q.size()){
		int u = q.front();q.pop();
		inq[u] = 0;
		for(int i = head[u];i != -1;i = e[i].nxt){
			int v = e[i].v;
			if(!e[i].f) continue;
			if(dis[v] > dis[u] + e[i].w){
				dis[v] = dis[u] + e[i].w;
				flow[v] = min(e[i].f,flow[u]);
				pre[v] = i;
				if(!inq[v]) inq[v] = 1,q.push(v);
			}
		}
	}
	if(dis[ed] == inf) return -1;
	return flow[ed];
}
int mfmv(){
	int ans = 0;
	int d;
	int sumd = 0;
	while((d = spfa())!=-1){
		ans += dis[ed]*d;
		sumd += d;
		int v = ed;
		while(v!=st){
			e[pre[v]].f -= d;
			e[pre[v]^1].f += d;
			v = e[pre[v]].u;
		}
	}return ans;
}

有上下限的网络流:

1.无汇源可行流:
建立超级源点和汇点S和T,原图中每条边的容量变为(上限 - 下限),并记录每个点下限流量的流入和流出,S连向流入 > 流出,流入< 流出的点连向T,容量都为|流入-流出|,跑最大流,若最大流*2 = |流入-流出|的总和,则有可行流。
复杂度:最大流算法的复杂度
关键代码:(建图,判断)

while(m--){
		int u,v;
		ll low,up;
		scanf("%d%d%lld%lld",&u,&v,&low,&up);
		add(u,v,up-low);
		du[u] -= low;
		du[v] += low;
	}
	for(int i = 1;i <= n; ++i){
		if(du[i] > 0) sum += du[i],add(st,i,du[i]);
		else if(du[i] < 0) add(i,ed,-du[i]);
	}
//判断语句↓
	ll t = dinic();
	if(t < sum) {
		cout<<"没有可行流;
	}

2.有汇源的可行流
在汇点和源点连一条容量inf的边,转换成问题 1.无汇源可行流

3.有汇源最大可行流:
建超级源点和超级汇点按问题1跑一遍可行流,删掉汇点到源点的那条inf的边,超级源点设置为普通源点超级汇点设置成普通汇点,再跑一次最大流。

复杂度:最大流算法复杂度
关键代码:

void sol(){
	ll t = dinic();
	if(t < sum) {
		cout<<"不存在可行流\n";return;
	}
	e[cnt-2].f = 0;
	st = S;ed = T;
	t = dinic();
	cout<

4.有汇源最小可行流:
建超级源点和超级汇点按问题1跑一遍可行流,删掉汇点到源点的那条inf的边,超级源点设置为普通汇点超级汇点设置成普通源点,再跑一次最大流(得到可以回流的最大流量),答案就是可行流减去最大流。

关键代码:

void sol(){
	ll t = dinic();
	if(t < sum) {
		cout<<"没有可行流\n";return;
	}
	t = e[cnt-1].f;
	e[cnt-2].f = 0;
	st = T;ed = S;
	t = t - dinic();
	cout<

5.最小费用可行流
按照问题1的方式建图,跑最小费用流,并检查得到的最大流是否为可行流。

代码:(CCF201812-5 管道清洁)

#include
#include
#include
using namespace std;
const int maxn = 233;
const int inf = 0x3f3f3f3f;
struct node{
	int u,v,f,w,nxt;
	node(){};
	node(int a,int b,int c,int d,int e):u(a),v(b),f(c),w(d),nxt(e){}
}e[maxn*maxn];
int cnt = 0;
int head[maxn];
void add(int u,int v,int f,int w){
	e[cnt] = node(u,v,f,w,head[u]);
	head[u] = cnt++;
	e[cnt] = node(v,u,0,-w,head[v]);
	head[v] = cnt++;
}
int st,ed,E,ex;
int n,m;
int du[maxn];
int sum;
int num = 0;
void init(){
	scanf("%d%d",&n,&m);
	st = 0;ed = n + 1;
	ex = n + 2;
	cnt = 0;
	sum = 0;
	num = 0;
	memset(head,-1,ex<<2);
	memset(du,0,ex<<2);
	while(m--){
		int u,v;char c[2];
		scanf("%d%d%s",&u,&v,c);
		if(c[0] == 'A'){
			add(u,v,inf,E);
			du[u]--;
			du[v]++;
			num+=E;
		}
		else if(c[0] == 'B'){
			du[u]--;
			du[v]++;
			num+=E;
		}
		else if(c[0] == 'C'){
			add(u,v,inf,E);
		}
		else add(u,v,1,E);
	}
	for(int i = 1;i <= n;++i){
		if(du[i] > 0){
			sum += du[i];
			add(st,i,du[i],0);
		}
		else if(du[i] < 0){
			add(i,ed,-du[i],0);
		}
	}
}
int pre[maxn];
int flow[maxn];
int inq[maxn];
int dis[maxn];
int spfa(){
	memset(pre,-1,ex<<2);
	memset(inq,0,ex<<2);
	memset(dis,0x3f,ex<<2);
	queue q;q.push(st);
	flow[st] = inf;inq[st] = 1;
	dis[st] = 0;
	while(q.size()){
		int u = q.front();q.pop();
		inq[u] = 0;
		for(int i = head[u];i != -1; i = e[i].nxt){
			int v = e[i].v;
			if(e[i].f && dis[v] > dis[u] + e[i].w){
				dis[v] = dis[u] + e[i].w;
				pre[v] = i;
				flow[v] = min(flow[u],e[i].f);
				if(!inq[v]) inq[v] = 1,q.push(v);
			}
		}
	}
	if(pre[ed] == -1) return -1;
	return flow[ed];
}
int mfmv(){
	int fw = 0;
	int ans = 0;
	int d;
	while((d = spfa())!=-1){
		fw += d;
		ans += dis[ed]*d;
		int v = ed;
		while(v!=st){
			e[pre[v]].f -= d;
			e[pre[v]^1].f += d;
			v = e[pre[v]].u;
		}
	}
	if(fw!=sum) return -1;
	return ans + num;
}
void sol(){
	int ans = mfmv();
	printf("%d\n",ans);
}
int main(){
	int T;cin>>T;cin>>E>>E;
	while(T--){
		init();sol();
	}
} 

你可能感兴趣的:(图论)