zoukankan      html  css  js  c++  java
  • 网络流模板

    EK求最大流

    (O(nm^2)), 一般可以处理(1000)~(10000)的网络
    每次增广一条路径

    #include <bits/stdc++.h>
    using namespace std;
    
    const int N = 1e3 + 10;
    const int M = 2e4 + 10;
    const int INF = 1e9;
    
    int n, m, S, T;
    struct edge
    {
    	int to, nxt, flow;
    }line[M];
    int fist[N], idx;
    int d[N], pre[N];
    bool st[N];
    
    void add(int x, int y, int z)
    {
    	line[idx] = {y, fist[x], z};
    	fist[x] = idx ++;
    	line[idx] = {x, fist[y], 0};
    	fist[y] = idx ++;	
    }
    
    bool bfs()
    {
    	queue<int> q;
    	memset(st, 0, sizeof st);
    	q.push(S), st[S] = 1, d[S] = INF;
    	while(!q.empty())
    	{
    		int u = q.front(); q.pop();
    		for(int i = fist[u]; i != -1; i = line[i].nxt)
    		{
    			int v = line[i].to;
    			if(!st[v] && line[i].flow)
    			{
    				st[v] = 1;
    				d[v] = min(line[i].flow, d[u]);
    				pre[v] = i;
    				if(v == T) return 1;
    				q.push(v);
    			}
    		} 
    	}	
    	return 0;
    } 
    
    int EK()
    {
    	int res = 0;
    	while(bfs())
    	{
    		res += d[T];
    		for(int i = T; i != S; i = line[pre[i] ^ 1].to)
    			line[pre[i]].flow -= d[T],
    			line[pre[i] ^ 1].flow += d[T];
    	}
    	return res;
    } 
    
    int main()
    {
    	scanf("%d%d%d%d", &n, &m, &S, &T);
    	memset(fist, -1, sizeof fist);
    	for(int i = 1; i <= m; ++ i)
    	{
    		int a, b, c;
    		scanf("%d%d%d", &a, &b, &c);
    		add(a, b, c);
    	}
    	printf("%d
    ", EK());
    	return 0; 
    } 
    

    Dinic求最大流/最小割

    (O(n^2m)), 一般可以处理(10000)~(100000)的网络
    每次尽可能多的增广路径
    防止在环上陷入死循环,使用分层图,处理每个点和起点间的距离

    #include <bits/stdc++.h>
    using namespace std;
    
    const int N = 1e4 + 10;
    const int M = 2e5 + 10;
    const int INF = 1e9;
    
    int n, m, S, T;
    struct Edge
    {
    	int to, nxt, flow;
    }line[M];
    int fist[N], idx;
    int d[N], cur[N];
    
    void add(int x, int y, int z)
    {
    	line[idx] = {y, fist[x], z};
    	fist[x] = idx ++;
    	line[idx] = {x, fist[y], 0};
    	fist[y] = idx ++;
    }
    
    bool bfs()
    {
    	queue<int> q;
    	memset(d, -1, sizeof d);	
    	q.push(S), d[S] = 0, cur[S] = fist[S];
    	while(!q.empty())
    	{
    		int u = q.front(); q.pop();
    		for(int i = fist[u]; i != -1; i = line[i].nxt)
    		{
    			int v = line[i].to;
    			if(d[v] == -1 && line[i].flow)
    			{
    				d[v] = d[u] + 1;
    				cur[v] = fist[v];
    				if(v == T) return 1;
    				q.push(v); 
    			}
    		}
    	}
    	return 0;
    }
    
    int find(int u, int limit)
    {
    	if(u == T) return limit;
    	int flow = 0;
    	for(int i = cur[u]; i != -1 && flow < limit; i = line[i].nxt)
    	{
    		cur[u] = i;
    		int v = line[i].to;
    		if(d[v] == d[u] + 1 && line[i].flow)
    		{
    			int t = find(v, min(line[i].flow, limit - flow));
    			if(!t) d[v] = -1;
    			line[i].flow -= t;
    			line[i ^ 1].flow += t;
    			flow += t;
    		}
    	}
    	return flow;
    }
    
    int dinic()
    {
    	int res = 0, flow;
    	while(bfs()) while(flow = find(S, INF)) res += flow;
    	return res;
    }
    
    int main()
    {
    	scanf("%d%d%d%d", &n, &m, &S, &T);
    	memset(fist, -1, sizeof fist);
    	for(int i = 1; i <= m; ++ i)
    	{
    		int a, b, c;
    		scanf("%d%d%d", &a, &b, &c);
    		add(a, b, c);
    	}
    	printf("%d
    ", dinic());
    	return 0;
    }
    

    无源汇上下界可行流

    每条边的容量变成容量上界-容量下界
    (c_1) 为该点少流入的流量,(c_2) 为该点少流出的流量
    (c_1) > (c_2) ,从源点向该点连一条容量为 (c_1 - c_2)的边
    (c_1) < (c_2) ,从该点向汇点连一条容量为 (c_2 - c_1)的边

    #include <bits/stdc++.h>
    using namespace std;
    
    const int N = 200 + 10;
    const int M = (10200 + N) * 2;
    const int INF = 1e9;
    
    int n, m, S, T;
    struct Edge
    {
    	int to, nxt, flow;
    }line[M];
    int fist[N], idx;
    int cur[N], d[N];
    int A[N], l[M];
    
    void add(int x, int y, int lz, int uz)
    {
    	line[idx] = {y, fist[x], uz - lz};
    	l[idx] = lz;
    	fist[x] = idx ++;
    	line[idx] = {x, fist[y], 0};
    	fist[y] = idx ++;
    }
    
    bool bfs()
    {
    	queue<int> q;
    	memset(d, -1, sizeof d);	
    	q.push(S), d[S] = 0, cur[S] = fist[S];
    	while(!q.empty())
    	{
    		int u = q.front(); q.pop();
    		for(int i = fist[u]; i != -1; i = line[i].nxt)
    		{
    			int v = line[i].to;
    			if(d[v] == -1 && line[i].flow)
    			{
    				d[v] = d[u] + 1;
    				cur[v] = fist[v];
    				if(v == T) return 1;
    				q.push(v);
    			}
    		} 
    	}
    	return 0;
    }
    
    int find(int u, int limit)
    {
    	if(u == T) return limit;
    	int flow = 0;
    	for(int i = cur[u]; i != -1 && flow < limit; i = line[i].nxt)
    	{
    		cur[u] = i;
    		int v = line[i].to;
    		if(d[v] == d[u] + 1 && line[i].flow)
    		{
    			int t = find(v, min(line[i].flow, limit - flow));
    			if(!t) d[v] = -1;
    			line[i].flow -= t;
    			line[i ^ 1].flow += t;
    			flow += t;
    		}
    	}
    	return flow;
    }
    
    int dinic()
    {
    	int res = 0, flow;
    	while(bfs()) while(flow = find(S, INF)) res += flow;
    	return res;	
    }
    
    int main()
    {
    	scanf("%d%d", &n, &m);
    	memset(fist, -1, sizeof fist);
    	S = 0, T = n + 1;
    	for(int i = 1; i <= m; ++ i)
    	{
    		int a, b, c, d;
    		scanf("%d%d%d%d", &a, &b, &c, &d);
    		add(a, b, c, d);
    		A[b] += c, A[a] -= c;
    	}
    	int tot = 0;
    	for(int i = 1; i <= n; ++ i)
    		if(A[i] > 0) add(S, i, 0, A[i]), tot += A[i];
    		else if(A[i] < 0) add(i, T, 0, -A[i]);
    	if(dinic() != tot) puts("NO");
    	else 
    	{
    		puts("YES");
    		for(int i = 0; i < m * 2; i += 2)
    			printf("%d
    ", line[i ^ 1].flow + l[i]);
    	} 
    	return 0;
    }  
    

    有源汇上下界最大流

    从汇点向源点连一条容量为 (+infty) 的边,使得所有点流量守恒
    按照无源汇上下界可行流的求法建立虚拟源点和汇点,求新图的最大流,判断是否满流
    在残留网络上删去汇点到源点容量为 (+infty) 的边, 跑一遍从源点到汇点的最大流
    答案为原网络的可行流加上该最大流

    #include <bits/stdc++.h>
    using namespace std;
    
    const int N = 200 + 10;
    const int M = (10000 + N) * 2;
    const int INF = 1e9; 
    
    int n, m, S, T;
    struct Edge
    {
    	int to, nxt, flow;
    }line[M];
    int fist[N], idx;
    int d[N], cur[N], A[N];
    
    void add(int x, int y, int z)
    {
    	line[idx] = {y, fist[x], z};
    	fist[x] = idx ++;
    	line[idx] = {x, fist[y], 0};
    	fist[y] = idx ++;
    }
    
    bool bfs()
    {
    	queue<int> q;
    	memset(d, -1, sizeof d);
    	q.push(S), d[S] = 0, cur[S] = fist[S];
    	while(!q.empty())
    	{
    		int u = q.front(); q.pop();
    		for(int i = fist[u]; i != -1; i = line[i].nxt)
    		{
    			int v = line[i].to;
    			if(d[v] == -1 && line[i].flow)
    			{
    				d[v] = d[u] + 1;
    				cur[v] = fist[v];
    				if(v == T) return 1;
    				q.push(v);  
    			}
    		} 
    	}
    	return 0;
    }
    
    int find(int u, int limit)
    {
    	if(u == T) return limit;
    	int flow = 0;
    	for(int i = cur[u]; i != -1 && flow < limit; i = line[i].nxt)
    	{
    		cur[u] = i;
    		int v = line[i].to;
    		if(d[v] == d[u] + 1 && line[i].flow)
    		{
    			int t = find(v, min(line[i].flow, limit - flow));
    			if(!t) d[v] = -1;
    			line[i].flow -= t;
    			line[i ^ 1].flow += t;
    			flow += t;
    		}
    	}
    	return flow;
    }
    
    int dinic()
    {
    	int res = 0, flow;
    	while(bfs()) while(flow = find(S, INF)) res += flow;
    	return res;
    }
    
    int main()
    {
    	int s, t;
    	scanf("%d%d%d%d", &n, &m, &s, &t);
    	memset(fist, -1, sizeof fist);
    	S = 0, T = n + 1;
    	
    	for(int i = 1; i <= m; ++ i)
    	{
    		int a, b, c, d;
    		scanf("%d%d%d%d", &a, &b, &c, &d);
    		add(a, b, d - c);
    		A[a] -= c, A[b] += c; 
    	} 
    	
    	int tot = 0;
    	for(int i = 1; i <= n; ++ i)
    		if(A[i] > 0) add(S, i, A[i]), tot += A[i];
    		else if(A[i] < 0) add(i, T, -A[i]);
    	
    	add(t, s, INF);
    	
    	if(dinic() != tot) puts("No Solution"); 
    	else 
    	{
    		int res = line[idx - 1].flow;
    		S = s, T = t;
    		line[idx - 1].flow = line[idx - 2].flow = 0;
    		printf("%d
    ", res + dinic());
    	}
    	return 0;
    }
    

    有源汇上下界最小流

    和有源汇上下界最大流做法类似,最后在残留网络上跑一遍汇点到源点的最大流即可
    答案为原网络可行流减去该最大流

    #include <bits/stdc++.h>
    using namespace std;
    
    const int N = 50010;
    const int M = (N + 125003) * 2;
    const int INF = 0x3f3f3f3f;
    
    int n, m, S, T;
    struct Edge
    {
    	int to, nxt, flow;
    }line[M];
    int fist[N], idx;
    int cur[N], d[N];
    int A[N];
    
    void add(int x, int y, int z)
    {
    	line[idx] = {y, fist[x], z};
    	fist[x] = idx ++;
    	line[idx] = {x, fist[y], 0};
    	fist[y] = idx ++;
    } 
    
    bool bfs()
    {
    	queue<int> q;
    	memset(d, -1, sizeof d);
    	q.push(S), d[S] = 0, cur[S] = fist[S];
    	while(!q.empty())
    	{
    		int u = q.front(); q.pop();
    		for(int i = fist[u]; i != -1; i = line[i].nxt)
    		{
    			int v = line[i].to;
    			if(d[v] == -1 && line[i].flow)
    			{
    				d[v] = d[u] + 1;
    				cur[v] = fist[v];
    				if(v == T) return 1;
    				q.push(v);
    			}
    		}
    	}
    	return 0;
    }
    
    int find(int u, int limit)
    {
    	if(u == T) return limit;
    	int flow = 0;
    	for(int i = cur[u]; i != -1 && flow < limit; i = line[i].nxt)
    	{
    		cur[u] = i;
    		int v = line[i].to;
    		if(d[v] == d[u] + 1 && line[i].flow)
    		{
    			int t = find(v, min(line[i].flow, limit - flow));
    			if(!t) d[v] = -1;
    			line[i].flow -= t;
    			line[i ^ 1].flow += t;
    			flow += t;
    		}
    	}
    	return flow;
    }
    
    int dinic()
    {
    	int res = 0, flow;
    	while(bfs()) while(flow = find(S, INF)) res += flow;
    	return res; 
    }
    
    int main()
    {
    	int s, t;
    	scanf("%d%d%d%d", &n, &m, &s, &t);
    	S = 0, T = n + 1;
    	memset(fist, -1, sizeof fist);
    	
    	for(int i = 1; i <= m; ++ i)
    	{
    		int a, b, c, d;
    		scanf("%d%d%d%d", &a, &b, &c, &d);
    		add(a, b, d - c);
    		A[a] -= c, A[b] += c; 
    	}
    	
    	int tot = 0;
    	for(int i = 1; i <= n; ++ i)
    		if(A[i] > 0) add(S, i, A[i]), tot += A[i];
    		else if(A[i] < 0) add(i, T, -A[i]);
    	
    	add(t, s, INF);
    	
    	if(dinic() != tot) puts("No Solution");
    	else 
    	{
    		int res = line[idx - 1].flow;
    		S = t, T = s;
    		line[idx - 1].flow = line[idx - 2].flow = 0;
    		printf("%d
    ", res - dinic());
    	}
    	return 0;
    } 
    

    多源汇最大流

    建立一个超级源点,从超级源点向所有源点连一条容量为 (+infty) 的边
    建立一个超级汇点,从所有汇点向超级汇点连一条容量为 (+infty) 的边

    #include <bits/stdc++.h>
    using namespace std;
    
    const int N = 10010;
    const int M = (100000 + N) * 2;
    const int INF = 1e9;
    
    int n, m, S, T;
    struct Edge
    {
    	int to, nxt, flow; 
    }line[M];
    int fist[N], idx;
    int cur[N], d[N];
    
    void add(int x, int y, int z)
    {
    	line[idx] = {y, fist[x], z};
    	fist[x] = idx ++;
    	line[idx] = {x, fist[y], 0};
    	fist[y] = idx ++;
    }
    
    bool bfs()
    {
    	queue<int> q;
    	memset(d, -1, sizeof d);
    	q.push(S), d[S] = 0, cur[S] = fist[S];
    	while(!q.empty())
    	{
    		int u = q.front(); q.pop();
    		for(int i = fist[u]; i != -1; i = line[i].nxt)
    		{
    			int v = line[i].to;
    			if(d[v] == -1 && line[i].flow)
    			{
    				d[v] = d[u] + 1;
    				cur[v] = fist[v];
    				if(v == T) return 1;
    				q.push(v);
    			}
    		}
    	}
    	return 0;
    }
    
    int find(int u, int limit)
    {
    	if(u == T) return limit;
    	int flow = 0;
    	for(int i = cur[u]; i != -1 && flow < limit; i = line[i].nxt)
    	{
    		cur[u] = i;
    		int v = line[i].to;
    		if(d[v] == d[u] + 1 && line[i].flow)
    		{
    			int t = find(v, min(line[i].flow, limit - flow));
    			if(!t) d[v] = -1;
    			line[i].flow -= t;
    			line[i ^ 1].flow += t;
    			flow += t;
    		}
    	}
    	return flow;
    }
    
    int dinic()
    {
    	int res = 0, flow;
    	while(bfs()) while(flow = find(S, INF)) res += flow;
    	return res;
    }
    
    int main()
    {
    	int sc, tc;
    	scanf("%d%d%d%d", &n, &m, &sc, &tc);
    	S = 0, T = n + 1;
    	memset(fist, -1, sizeof fist);
    	for(int i = 1; i <= sc; ++ i)
    	{
    		int x;
    		scanf("%d", &x);
    		add(S, x, INF);
    	}
    	for(int i = 1; i <= tc; ++ i)
    	{
    		int x; 
    		scanf("%d", &x);
    		add(x, T, INF);
    	}
    	for(int i = 1; i <= m; ++ i)
    	{
    		int a, b, c;
    		scanf("%d%d%d", &a, &b, &c);
    		add(a, b, c);
    	}
    	printf("%d
    ", dinic());
    	return 0;
    } 
    

    最小费用最大流

    #include <bits/stdc++.h>
    using namespace std;
     
    const int N = 5000 + 10;
    const int M = (50000 + 10) * 2;
    const int INF = 1e9;
    
    int n, m, S, T;
    struct Edge
    {
    	int to, nxt, flow, w; 
    }line[M];
    int fist[N], idx;
    int pre[N], incf[N], d[N];
    bool st[N];
    
    void add(int x, int y, int z, int w)
    {
    	line[idx] = {y, fist[x], z, w};
    	fist[x] = idx ++;
    	line[idx] = {x, fist[y], 0, -w};
    	fist[y] = idx ++;
    }
    
    bool spfa()
    {
    	queue<int> q;
    	memset(d, 0x3f, sizeof d);
    	memset(incf, 0, sizeof incf);
    	q.push(S), d[S] = 0, incf[S] = INF;
    	while(!q.empty())
    	{
    		int u = q.front(); q.pop();
    		st[u] = 0;
    		
    		for(int i = fist[u]; i != -1; i = line[i].nxt)
    		{
    			int v = line[i].to;
    			if(line[i].flow && d[v] > d[u] + line[i].w)
    			{
    				d[v] = d[u] + line[i].w;
    				pre[v] = i;
    				incf[v] = min(incf[u], line[i].flow);
    				if(!st[v])
    				{
    					q.push(v);
    					st[v] = 1;
    				}
    			}
    		}
    	}
    	return incf[T] > 0;
    }
    
    void EK(int& flow, int& cost)
    {
    	flow = cost = 0;
    	while(spfa())
    	{
    		int t = incf[T];
    		flow += t, cost += t * d[T];
    		for(int i = T; i != S; i = line[pre[i] ^ 1].to)
    		{
    			line[pre[i]].flow -= t;
    			line[pre[i] ^ 1].flow += t;
    		}
    	}
    }
    
    int main()
    {
    	scanf("%d%d%d%d", &n, &m, &S, &T);
    	memset(fist, -1, sizeof fist);
    	for(int i = 1; i <= m; ++ i)
    	{
    		int a, b, c, d;
    		scanf("%d%d%d%d", &a, &b, &c, &d);
    		add(a, b, c, d);
    	}
    	
    	int flow, cost;
    	EK(flow, cost);
    	printf("%d %d
    ", flow, cost);
    	
    	return 0;
    }
    
  • 相关阅读:
    CF1327D Infinite Path 抽象代数
    CF1426 ABCDEF 数学+思维+DP
    CF1332E Height All the Same 计数
    CF1327E Count The Blocks 简单计数
    【CF1304】Codeforces Round #620 (Div. 2) 【思维+回文+构造+ST表】
    【CF1301】Codeforces Round #619 (Div. 2) 【思维+贪心+模拟+构造+二维ST表】
    【CF1255A】Changing Volume【思维】
    Json Master masters JSON!
    Polyglot Translators: Let's do i18n easier! 一款国际化插件小助手!
    mns: Money Never Sleeps! 自己开发的一款 IDEA 插件介绍.
  • 原文地址:https://www.cnblogs.com/ooctober/p/14410991.html
Copyright © 2011-2022 走看看