• 网络流的模型和应用


    1. 最大流

    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    #include <queue>
    #define int long long
    using namespace std;
    int read(){
    	int x = 1,a = 0;char ch = getchar();
    	while (ch < '0'||ch > '9'){if (ch == '-') x = -1;ch = getchar();}
    	while (ch >= '0'&&ch <= '9'){a = a*10+ch-'0';ch = getchar();}
    	return x*a;
    }
    const int maxn = 1e6+10,inf = 1e18+7;
    int n,m,s,t;
    int head[maxn],tot = 1,dis[maxn],cur[maxn];
    struct node{
    	int to,nxt,w;
    }ed[maxn*2];
    void add(int u,int to,int w){
    	ed[++tot].w = w;
    	ed[tot].to = to;
    	ed[tot].nxt = head[u];
    	head[u] = tot;
    }
    bool BFS(){
    	queue<int> q;q.push(s);
    	memset(dis,0,sizeof(dis));dis[s] = 1;
    	while (!q.empty()){
    		int x = q.front();q.pop();
    		for (int i = head[x];i;i = ed[i].nxt){
    			int to = ed[i].to;
    			if (dis[to]||!ed[i].w) continue;
    			dis[to] = dis[x]+1;
    			q.push(to);
    		}
    	}
    	return dis[t];
    }
    int DFS(int x,int lim){
    	if (!lim||x == t) return lim;
    	int res = 0;
    	for (int &i = head[x];i;i = ed[i].nxt){
    		int to = ed[i].to;
    		if (dis[to] != dis[x]+1) continue;
    		int tmp = DFS(to,min(lim,ed[i].w));
    		lim -= tmp,ed[i].w -= tmp,ed[i^1].w += tmp,res += tmp;
    		if (!lim) break;
    	}
    	return res;
    }
    signed main(){
    	n = read(),m = read(),s = read(),t = read();
    	for (int i = 1;i <= m;i++){
    		int u = read(),v = read(),w = read();
    		add(u,v,w),add(v,u,0);
    	}
    	for (int i = 0;i <= tot;i++) cur[i] = head[i];
    	int ans = 0,tmp;
    	while (BFS()){
    		while (tmp = DFS(s,inf)) ans += tmp;
    		for (int i = 0;i <= tot;i++) head[i] = cur[i];
    	}
    	printf("%lld
    ",ans);
    	return 0;
    }
    

    2. 费用流

    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    #include <queue>
    #define ll long long
    using namespace std;
    ll read(){
    	int x = 1,a = 0;char ch = getchar();
    	while (ch < '0'||ch > '9'){if (ch == '-') x = -1;ch = getchar();}
    	while (ch >= '0'&&ch <= '9'){a = a*10+ch-'0';ch = getchar();}
    	return x*a;
    }
    const ll maxn = 1e6+10,inf = 1e18+7;
    ll n,m,s,t;
    ll head[maxn],tot = 1,dis[maxn],cur[maxn],vis[maxn],pre1[maxn],pre2[maxn];
    struct node{
    	ll to,nxt,w,lim;
    }ed[maxn*2];
    void add(ll u,ll to,ll lim,ll w){
    	ed[++tot].w = w;
    	ed[tot].to = to;
    	ed[tot].nxt = head[u];
    	ed[tot].lim = lim;
    	head[u] = tot;
    }
    bool SPFA(){
    	memset(vis,0,sizeof(vis));
    	for (int i = s;i <= t;i++) dis[i] = inf;
    	queue<ll> q;q.push(s);
    	dis[s] = 0,vis[s] = 1;
    	while (!q.empty()){
    		int x = q.front();q.pop();
    		vis[x] = 0;
    		for (int i = head[x];i;i = ed[i].nxt){
    			ll to = ed[i].to;
    			if (!ed[i].lim) continue;
    			if (dis[to] > dis[x]+ed[i].w){
    				dis[to] = dis[x]+ed[i].w;
    				pre1[to] = x,pre2[to] = i;
    				if (!vis[to]){
    					vis[to] = 1;
    					q.push(to);
    				}
    			}
    		}
    	}
    	return dis[t] != inf;
    }
    int main(){
    	n = read(),m = read();s = 1,t = n;
    	for (int i = 1;i <= m;i++){
    		int u = read(),v = read(),w = read(),lim = read();
    		add(u,v,w,lim),add(v,u,0,-lim);
    	}
    	ll ans1 = 0,ans2 = 0;
    	while (SPFA()){
    		ll tmp = inf;
    		for (int i = t;i != s;i = pre1[i]) tmp = min(tmp,ed[pre2[i]].lim);
    		ans1 += tmp,ans2 += tmp*dis[t];
    		for (int i = t;i != s;i = pre1[i]) ed[pre2[i]].lim -= tmp,ed[pre2[i]^1].lim += tmp;
    	}
    	printf("%lld %lld
    ",ans1,ans2);
    	return 0;
    }
    

    3. 最小割

    最小割 = 最大流

    4. 最大权闭合子图

    这个问题可以转化为最小割问题,用网络流解决。

    从源点s向每个正权点连一条容量为权值的边,每个负权点向汇点t连一条容量为权值的绝对值的边,有向图原来的边容量全部为无限大。求它的最小割,割掉后,与源点s连通的点构成最大权闭合子图,权值为(正权值之和-最小割)

    5. 无源汇上下界可行流

    记第i条边的下界为(down_i),上界为(up_i)

    我们先让每条边流下界的流量,即将每条边i的容量设为(up_i−down_i),下界为0,现在我们能满足下界的要求了,但是流量是不守恒的,建虚拟源点S和汇点T。

    我们记每个点x的入流量为(in_x),出流量为(out_x),之后我们根据(in_x)(out_x)的大小分类讨论:

    • (in_xgeq out_x),这就意味x点要多向外输出(in_x ext{-}out_x)的流量,我们从S向x连(in_x ext{-}out_x)容量的边。
    • (in_xleq out_x),这就意味x点要多从外输入(out_x ext{-}in_x)的流量,我们从x向T连(out_x ext{−}in_x)容量的边。

    之后我们求最大流,如果S流出的边有不满流的,就无解。

    对于边i,它的真实流量就是下界+这条边流过的流量(即反边流量)。

    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    #include <queue>
    #define int long long
    using namespace std;
    int read(){
    	int x = 1,a = 0;char ch = getchar();
    	while (ch < '0'||ch > '9'){if (ch == '-') x = -1;ch = getchar();}
    	while (ch >= '0'&&ch <= '9'){a = a*10+ch-'0';ch = getchar();}
    	return x*a;
    }
    const int maxn = 1e6+10,inf = 1e18+7;
    int n,m,s,t;
    int head[maxn],tot = 1,dis[maxn],cur[maxn],sum;
    struct ask{
    	int u,v,up,down;
    }e[maxn];
    int in[maxn],out[maxn];
    struct node{
    	int to,nxt,w;
    }ed[maxn*2];
    void add(int u,int to,int w){
    	ed[++tot].w = w;
    	ed[tot].to = to;
    	ed[tot].nxt = head[u];
    	head[u] = tot;
    }
    bool BFS(){
    	queue<int> q;q.push(s);
    	memset(dis,0,sizeof(dis));dis[s] = 1;
    	while (!q.empty()){
    		int x = q.front();q.pop();
    		for (int i = head[x];i;i = ed[i].nxt){
    			int to = ed[i].to;
    			if (dis[to]||!ed[i].w) continue;
    			dis[to] = dis[x]+1;
    			q.push(to);
    		}
    	}
    	return dis[t];
    }
    int DFS(int x,int lim){
    	if (!lim||x == t) return lim;
    	int res = 0;
    	for (int &i = head[x];i;i = ed[i].nxt){
    		int to = ed[i].to;
    		if (dis[to] != dis[x]+1) continue;
    		int tmp = DFS(to,min(lim,ed[i].w));
    		lim -= tmp,ed[i].w -= tmp,ed[i^1].w += tmp,res += tmp;
    		if (!lim) break;
    	}
    	return res;
    }
    signed main(){
    	n = read(),m = read(),s = 0,t = n+1;
    	for (int i = 1;i <= m;i++) e[i].u = read(),e[i].v = read(),e[i].down = read(),e[i].up = read();
    	for (int i = 1;i <= m;i++) in[e[i].v] += e[i].down,out[e[i].u] += e[i].down;
    	for (int i = 1;i <= m;i++) add(e[i].u,e[i].v,e[i].up-e[i].down),add(e[i].v,e[i].u,0);
    	for (int i = 1;i <= n;i++){
    		if (in[i] < out[i]) add(i,t,out[i]-in[i]),add(t,i,0);
    		if (in[i] > out[i]) add(s,i,in[i]-out[i]),add(i,s,0),sum += in[i]-out[i];
    	}
    	for (int i = 0;i <= tot;i++) cur[i] = head[i];
    	int ans = 0,tmp;
    	while (BFS()){
    		while (tmp = DFS(s,inf)) ans += tmp;
    		for (int i = 0;i <= tot;i++) head[i] = cur[i];
    	}
    	if (ans != sum) {printf("NO
    ");return 0;}
    	printf("YES
    ");
    	for (int i = 2;i <= 2*m;i+=2) printf("%lld
    ",ed[i^1].w+e[i/2].down);
    	return 0;
    }
    

    6. 有源汇上下界最大流

    初始从(t)(s)连一条[0,inf]的边,再把两个点和普通点一起向虚构源点和汇点仿无源连边,跑最大流判断是否有解,再从真实的(sRightarrow t)跑最大流求解

    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    #include <queue>
    #define int long long
    using namespace std;
    int read(){
    	int x = 1,a = 0;char ch = getchar();
    	while (ch < '0'||ch > '9'){if (ch == '-') x = -1;ch = getchar();}
    	while (ch >= '0'&&ch <= '9'){a = a*10+ch-'0';ch = getchar();}
    	return x*a;
    }
    const int maxn = 1e6+10,inf = 1e18+7;
    int n,m,s,t,st,sd,ans;
    int head[maxn],tot = 1,dis[maxn],cur[maxn],sum;
    struct ask{
    	int u,v,up,down;
    }e[maxn];
    int in[maxn],out[maxn];
    struct node{
    	int to,nxt,w;
    }ed[maxn*2];
    void add(int u,int to,int w){
    	ed[++tot].w = w;
    	ed[tot].to = to;
    	ed[tot].nxt = head[u];
    	head[u] = tot;
    }
    bool BFS(){
    	queue<int> q;q.push(s);
    	memset(dis,0,sizeof(dis));dis[s] = 1;
    	while (!q.empty()){
    		int x = q.front();q.pop();
    		for (int i = head[x];i;i = ed[i].nxt){
    			int to = ed[i].to;
    			if (dis[to]||!ed[i].w) continue;
    			dis[to] = dis[x]+1;
    			q.push(to);
    		}
    	}
    	return dis[t];
    }
    int DFS(int x,int lim){
    	if (!lim||x == t) return lim;
    	int res = 0;
    	for (int &i = head[x];i;i = ed[i].nxt){
    		int to = ed[i].to;
    		if (dis[to] != dis[x]+1) continue;
    		int tmp = DFS(to,min(lim,ed[i].w));
    		lim -= tmp,ed[i].w -= tmp,ed[i^1].w += tmp,res += tmp;
    		if (!lim) break;
    	}
    	return res;
    }
    signed main(){
    	n = read(),m = read(),st = read(),sd = read();s = 0,t = n+1;
    	for (int i = 1;i <= m;i++) e[i].u = read(),e[i].v = read(),e[i].down = read(),e[i].up = read();
    	for (int i = 1;i <= m;i++) in[e[i].v] += e[i].down,out[e[i].u] += e[i].down;
    	for (int i = 1;i <= m;i++) add(e[i].u,e[i].v,e[i].up-e[i].down),add(e[i].v,e[i].u,0);
    	for (int i = 1;i <= n;i++){
    		if (in[i] < out[i]) add(i,t,out[i]-in[i]),add(t,i,0);
    		if (in[i] > out[i]) add(s,i,in[i]-out[i]),add(i,s,0),sum += in[i]-out[i];
    	}
    	add(sd,st,inf),add(st,sd,0);
    	for (int i = 0;i <= tot;i++) cur[i] = head[i];
    	int tmp;
    	while (BFS()){
    		while (tmp = DFS(s,inf)) ans += tmp;
    		for (int i = 0;i <= tot;i++) head[i] = cur[i];
    	}
    	if (ans < sum) {printf("please go home to sleep
    ");return 0;}
    	s = st,t = sd,ans = ed[tot].w;
    	ed[tot].w = ed[tot^1].w = 0;
    	while (BFS()){
    		while (tmp = DFS(s,inf)) ans += tmp;
    		for (int i = 0;i <= tot;i++) head[i] = cur[i];
    	}
        printf("%lld
    ",ans);
    	return 0;
    }
    

    7. 有源汇上下界最小流

    和最大流差不多

    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    #include <queue>
    #define int long long
    using namespace std;
    int read(){
    	int x = 1,a = 0;char ch = getchar();
    	while (ch < '0'||ch > '9'){if (ch == '-') x = -1;ch = getchar();}
    	while (ch >= '0'&&ch <= '9'){a = a*10+ch-'0';ch = getchar();}
    	return x*a;
    }
    const int maxn = 1e6+10,inf = 1e18+7;
    int n,m,s,t,st,sd,ans;
    int head[maxn],tot = 1,dis[maxn],cur[maxn],sum;
    struct ask{
    	int u,v,up,down;
    }e[maxn];
    int in[maxn],out[maxn];
    struct node{
    	int to,nxt,w;
    }ed[maxn*2];
    void add(int u,int to,int w){
    	ed[++tot].w = w;
    	ed[tot].to = to;
    	ed[tot].nxt = head[u];
    	head[u] = tot;
    }
    bool BFS(){
    	queue<int> q;q.push(s);
    	memset(dis,0,sizeof(dis));dis[s] = 1;
    	while (!q.empty()){
    		int x = q.front();q.pop();
    		for (int i = head[x];i;i = ed[i].nxt){
    			int to = ed[i].to;
    			if (dis[to]||!ed[i].w) continue;
    			dis[to] = dis[x]+1;
    			q.push(to);
    		}
    	}
    	return dis[t];
    }
    int DFS(int x,int lim){
    	if (!lim||x == t) return lim;
    	int res = 0;
    	for (int &i = head[x];i;i = ed[i].nxt){
    		int to = ed[i].to;
    		if (dis[to] != dis[x]+1) continue;
    		int tmp = DFS(to,min(lim,ed[i].w));
    		lim -= tmp,ed[i].w -= tmp,ed[i^1].w += tmp,res += tmp;
    		if (!lim) break;
    	}
    	return res;
    }
    signed main(){
    	n = read(),m = read(),st = read(),sd = read();s = 0,t = n+1;
    	for (int i = 1;i <= m;i++) e[i].u = read(),e[i].v = read(),e[i].down = read(),e[i].up = read();
    	for (int i = 1;i <= m;i++) in[e[i].v] += e[i].down,out[e[i].u] += e[i].down;
    	for (int i = 1;i <= m;i++) add(e[i].u,e[i].v,e[i].up-e[i].down),add(e[i].v,e[i].u,0);
    	for (int i = 1;i <= n;i++){
    		if (in[i] < out[i]) add(i,t,out[i]-in[i]),add(t,i,0);
    		if (in[i] > out[i]) add(s,i,in[i]-out[i]),add(i,s,0),sum += in[i]-out[i];
    	}
    	for (int i = 0;i <= tot;i++) cur[i] = head[i];
    	int tmp;
    	while (BFS()){
    		while (tmp = DFS(s,inf)) ans += tmp;
    		for (int i = 0;i <= tot;i++) head[i] = cur[i];
    	}
    	add(sd,st,inf),add(st,sd,0);
    	while (BFS()){
    		while (tmp = DFS(s,inf)) ans += tmp;
    		for (int i = 0;i <= tot;i++) head[i] = cur[i];
    	}
    	if (ans < sum) {printf("please go home to sleep
    ");return 0;}
        printf("%lld
    ",ed[tot].w);
    	return 0;
    }
    

    8. 图的模型

  • 相关阅读:
    【递归】斐波那契数列第n个数
    二分查找【循环和递归】
    递归:正序、逆序输出一个正整数的各位数字
    Ubuntu x86-64汇编(5) 控制指令
    Ubuntu x86-64汇编(4) 数值操作指令
    Ubuntu x86-64汇编(3) 数值操作指令
    给X240换上了三键触摸板
    Ubuntu x86-64汇编(2)
    Ubuntu x86-64汇编(1)
    X240 Ubuntu18.04安装流水帐
  • 原文地址:https://www.cnblogs.com/little-uu/p/14361498.html
Copyright © 2020-2023  润新知