首页 > 其他分享 >【知识】网络流模板梳理&题型总结

【知识】网络流模板梳理&题型总结

时间:2024-11-30 15:11:00浏览次数:4  
标签:题型 idx int ne add include hh 梳理 模板

基础知识OI-Wiki网络流24题大佬博客

模板:

EK 求最大流

here
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 1005, M = 20005,INF=1e8;
int n, m, S, T;
int h[N], e[M], f[M], ne[M], idx;
int q[N], d[N], pre[N];
bool st[N];
void add(int a,int b,int c){
    e[idx] = b, f[idx] = c, ne[idx] = h[a], h[a] = idx++;
    e[idx] = a, f[idx] = 0, ne[idx] = h[b], h[b] = idx++;
}

bool bfs(){
    int hh = 0, tt = 0;
    memset(st, 0, sizeof(st));
    q[0] = S, st[S] = 1,d[S] = INF;

    while(hh<=tt){
        int t = q[hh++];
        for (int i = h[t]; ~i;i=ne[i]){
            int ver = e[i];
            if(!st[ver]&&f[i]){
                st[ver] = 1;
                d[ver] = min(d[t], f[i]);
                pre[ver] = i;
                if(ver==T) return 1;
                    q[++tt] = ver;
            }
        }
    }
    return 0;
}
int EK(){
    int r = 0; 
    while(bfs()){
        r += d[T];
        for (int i = T; i != S;i=e[pre[i]^1])
            f[pre[i]] -= d[T], f[pre[i]^1] += d[T];
    }
    return r;
}
int main(){
    memset(h, -1, sizeof(h));
    cin >> n >> m >> S >> T;
    while(m--){
        int a, b, c;
        cin >> a >> b >> c;
        add(a, b, c);
    }
    cout << EK() << endl;
    return 0;
}

Dinic 求最大流

here
#include <bits/stdc++.h>
using namespace std;

const int N = 10005, M = 200005, INF = 1e8;
int n, m, S, T;
int h[N], e[M], f[M], ne[M], idx;
int q[N], d[N], cur[N];

void add(int a,int b,int c){
    e[idx] = b, f[idx] = c, ne[idx] = h[a], h[a] = idx++;
}
bool bfs(){
    int hh = 0, tt = 0;
    memset(d, -1, sizeof(d));
    q[0] = S, d[S] = 0, cur[S] = h[S];
    while(hh<=tt){
        int t = q[hh++];
        for (int i = h[t]; ~i;i=ne[i]){
            int ver = e[i];
            if(d[ver]==-1&&f[i]){
                d[ver] = d[t] + 1;
                cur[ver] = h[ver];
                if(ver==T)
                    return 1;
                q[++tt] = ver;
            }
        }
    }
    return 0;
}

int find(int u,int limit){
    if(u==T)
        return limit;
    int flow = 0;
    for (int i = cur[u]; ~i&&flow<limit;i=ne[i]){
        cur[u] = i;
        int ver = e[i];
        if(d[ver]==d[u]+1&&f[i]){
            int t = find(ver, min(f[i], limit - flow));
            if(!t) d[ver]=-1;
            f[i] -= t, f[i ^ 1] += t;
            flow += t;
        }
    }
    return flow;
}
int dinic(){
    int r = 0, flow;
    while(bfs()){
        while(flow=find(S,INF))
            r += flow;
    }
    return r;
}
int main(){
    memset(h, -1, sizeof(h));
    cin >> n >> m >> S >> T;
    while(m--){
        int a, b, c;
        cin >> a >> b >> c;
        add(a, b, c);
        add(b, a, 0);
    }

    cout << dinic() << endl;
    return 0;
}

匈牙利算法

here
#include <bits/stdc++.h>
using namespace std;
const int N = 10005, M = 200005;
int h[N], ne[M], cnt, e[M];
int match[N],vis[N];
int ans;
void add(int a,int b){
    e[++cnt] = b,ne[cnt] = h[a],h[a] = cnt;
}
bool dfs(int x){
    for (int i = h[x]; i;i=ne[i]){
        int to = e[i];
        if(vis[to])
            continue;
        vis[to] = 1;
        if(match[to]==0||dfs(match[to])){
            match[to] = x;
            return true;
        }
    }
    return false;
}
int main(){
    int n, m, e;
    cin >> n >> m >> e;
    for (int i = 1; i <= e;i++){
        int u, v;
        cin >> u >> v;
        add(u, v + n);
        add(v + n, u);
    }
    for (int i = 1; i <= n;i++){
        memset(vis, 0, sizeof(vis));
        if(dfs(i))
            ans++;
    }
    cout << ans << endl;
    return 0;
}

题型思路总结:

  • P2756 飞行员配对方案问题

  • 思路:

    • 解法一:
      观察到给定的图是一个二分图,求的就是二分图的最大匹配即可,时间复杂度 \(\mathcal{O}(nm)\)
    • 解法二:
      网络流算法。从源点 \(S\) 向 点集 \(N\) 的每个点连接一个容量为 \(1\) 的边,代表每名飞行员只能匹配一次。 从点集 \(M\) 向汇点 \(T\) 连接一个容量为 \(1\) 的边,因为能量守恒,所以代表 \(M\) 点集里的同一个点不能多次使用,因为一旦多次使用会违背能量守恒定律。建图后跑 dinic 求最大流即可。时间复杂度 \(\mathcal{O}(\sqrt nm)\)
  • 代码:

    here
    #include <bits/stdc++.h>
    using namespace std;
    
    const int N = 10005, M = 200005, INF = 1e8;
    int n, m, S, T;
    int h[N], e[M], f[M], ne[M], idx;
    int q[N], d[N], cur[N];
    
    void add(int a,int b,int c){
    	e[idx] = b, f[idx] = c, ne[idx] = h[a], h[a] = idx++;
    }
    bool bfs(){
    	int hh = 0, tt = 0;
    	memset(d, -1, sizeof(d));
    	q[0] = S, d[S] = 0, cur[S] = h[S];
    	while(hh<=tt){
    		int t = q[hh++];
    		for (int i = h[t]; ~i;i=ne[i]){
    			int ver = e[i];
    			if(d[ver]==-1&&f[i]){
    				d[ver] = d[t] + 1;
    				cur[ver] = h[ver];
    				if(ver==T)
    					return 1;
    				q[++tt] = ver;
    			}
    		}
    	}
    	return 0;
    }
    
    int find(int u,int limit){
    	if(u==T)
    		return limit;
    	int flow = 0;
    	for (int i = cur[u]; ~i&&flow<limit;i=ne[i]){
    		cur[u] = i;
    		int ver = e[i];
    		if(d[ver]==d[u]+1&&f[i]){
    			int t = find(ver, min(f[i], limit - flow));
    			if(!t) d[ver]=-1;
    			f[i] -= t, f[i ^ 1] += t;
    			flow += t;
    		}
    	}
    	return flow;
    }
    int dinic(){
    	int r = 0, flow;
    	while(bfs()){
    		while(flow=find(S,INF))
    			r += flow;
    	}
    	return r;
    }
    int main(){
    	memset(h, -1, sizeof(h));
    	cin >> m >> n;
    	S = 0, T = n + 1;
    	for (int i = 1; i <= m;i++)
    		add(S, i, 1), add(i, S, 0);
    	for (int i = m + 1; i <= n;i++)
    		add(i, T, 1), add(T, i, 0);
    	int a, b;
    	while(cin>>a>>b,a!=-1)
    		add(a, b, 1), add(b, a, 0);
    	cout << dinic() << endl;
    
    	for (int i = 0; i < idx;i+=2){
    		if(e[i]>m&&e[i]<=n&&!f[i])
    			cout << e[i ^ 1] << " " << e[i] << endl;
    	}
    		return 0;
    }
    

  • P3254 圆桌问题

  • 思路:

    • 建一个超级源点,向每个单位连一条为单位人数的边
    • 建个单位向每个桌子连一条为1的边(同一个单位的人不能在同一个桌子上)
    • 建一个超级汇点,每个桌子向汇点连一条为桌子容量的边

    跑一遍最大流,如果最大流量等于所有单位人数之和,则存在解,否则无解。

    输出方案:从每个单位出发的所有满流边指向的桌子就是该单位人员的安排情况。

  • 代码:

    here
      #include <bits/stdc++.h>
      using namespace std;
      const int N = 430, M = (150 * 270 + N) * 2, INF = 1e8;
      int m, n, S, T;
      int h[N], e[M], f[M], ne[M], idx;
      int q[N], d[N], cur[N];
      void add(int a, int b, int c){
      	e[idx] = b, f[idx] = c, ne[idx] = h[a], h[a] = idx ++ ;
      	e[idx] = a, f[idx] = 0, ne[idx] = h[b], h[b] = idx ++ ;
      }
    
      bool bfs(){
      	int hh = 0, tt = 0;
      	memset(d, -1, sizeof d);
      	q[0] = S, d[S] = 0, cur[S] = h[S];
      	while (hh <= tt){
      		int t = q[hh ++ ];
      		for (int i = h[t]; ~i; i = ne[i]){
      			int ver = e[i];
      			if (d[ver] == -1 && f[i]){
      				d[ver] = d[t] + 1;
      				cur[ver] = h[ver];
      				if (ver == T) return true;
      				q[ ++ tt] = ver;
      			}
      		}
      	}
      	return false;
      }
    
      int find(int u, int limit){
      	if (u == T) return limit;
      	int flow = 0;
      	for (int i = cur[u]; ~i && flow < limit; i = ne[i]){
      		cur[u] = i;
      		int ver = e[i];
      		if (d[ver] == d[u] + 1 && f[i]){
      			int t = find(ver, min(f[i], limit - flow));
      			if (!t) d[ver] = -1;
      			f[i] -= t, f[i ^ 1] += t, flow += t;
      		}
      	}
      	return flow;
      }
    
      int dinic(){
      	int r = 0, flow;
      	while (bfs()) while (flow = find(S, INF)) r += flow;
      	return r;
      }
    
      int main(){
      	scanf("%d%d", &m, &n);
      	S = 0, T = m + n + 1;
      	memset(h, -1, sizeof h);
    
      	int tot = 0;
      	for (int i = 1; i <= m; i ++ ){
      		int c;
      		scanf("%d", &c);
      		add(S, i, c);
      		tot += c;
      	}
      	for (int i = 1; i <= n; i ++ ){
      		int c;
      		scanf("%d", &c);
      		add(m + i, T, c);
      	}
      	for (int i = 1; i <= m; i ++ )
      		for (int j = 1; j <= n; j ++ )
      			add(i, m + j, 1);
    
      	if (dinic() != tot) puts("0");
      	else{
      		puts("1");
      		for (int i = 1; i <= m; i ++ ){
      			for (int j = h[i]; ~j; j = ne[j])
      				if (e[j] > m && e[j] <= m + n && !f[j])
      					printf("%d ", e[j] - m);
      			puts("");
      		}
      	}
    
      	return 0;
      }
    
  • 上下界网络流

    设 \(\operatorname{l}(u,\,v)\) 为 \(u \to v\) 的下界函数,特别的,当 \(u \to v \notin {\mathrm E}\) 时,\(\operatorname{l}(u,\,v) = 0\)。

    定义图 \(\mathrm G = \langle \mathrm {V,\,E} \rangle\) 上的流函数 \(\operatorname{f} : \mathrm {V \times V} \to \mathbb R\),满足如下限制:

    • 容量限制: 对于 \(\forall u,\,v \in {\mathrm V}\) 有 \(\operatorname{l}(u,\,v) \le \operatorname{f}(u,\,v) \le \operatorname{c}(u,\,v)\)。
    • 流量守恒: 对于 \(\forall u \in {\mathrm{V}}-\{s,\,t\}\),要求:\(\begin{aligned} & \sum_{v \in {\mathrm{V}}}\operatorname{f}(u,\,v)=\sum_{v \in {\mathrm{V}}}\operatorname{f}(v,\,u) \end{aligned}\)

    则称非负值 \(\operatorname{f}(u,\,v)\) 为图 \(\mathrm {G = \langle V,\,E \rangle}\) 的一个可行流

    一个流的流值 \(|f|\) 定义如下:

    \[\begin{aligned} & |f| = \sum_{v \in {\mathrm{V}}}\operatorname{f}(s,\,v)-\sum_{v \in {\mathrm{V}}}\operatorname{f}(v,\,s) \end{aligned} \]

  • 无源汇上下界可行流

    给定一个特殊点 \(s\) 和一个特殊点 \(t\),找出从 \(s\) 到 \(t\) 的一个流 \(\operatorname{f}(s,\,t)\),使得 \(|f|\) 的值最大。

    无源汇上下界可行流就是没有源汇的情况下,求出图 \(\mathrm G = \langle \mathrm{V,\,E} \rangle\) 的一个可行流。

    对于每条边,我们可以用上界减去下界得到差值网络

    建模方法如下:

    • 设 \(\operatorname{in}(u)\) 为 \(u\) 的所有入边的容量的总和,\(\operatorname{out}(u)\) 为 \(u\) 的所有出边的容量的总和。
    • 计算 \(w = \operatorname{in}(u)-\operatorname{out}(u)\)。
    • 如果 \(w > 0\),则从超级源点 \(s\) 向 \(u\) 连一条容量为 \(w\) 的边。如果 \(w <0\),则从 \(u\) 向超级汇点 \(t\) 连容量为 \(-w\) 的边。
    • 跑最大流,如果存在附加边没满流,则不存在可行流,否则 \(\mathrm maxflow\) 加上所有边的下界的和即为可行流的流值。

    添加附加边维护了平衡条件,加上 下界网络就相当于得到了一个流量平衡的残余网络。

    here
    #include <iostream>
    #include <cstring>
    #include <algorithm>
    
    using namespace std;
    
    const int N = 210, M = (10200 + N) * 2, INF = 1e8;
    
    int n, m, S, T;
    int h[N], e[M], f[M], l[M], ne[M], idx;
    int q[N], d[N], cur[N], A[N];
    
    void add(int a, int b, int c, int d)
    {
        e[idx] = b, f[idx] = d - c, l[idx] = c, ne[idx] = h[a], h[a] = idx ++ ;
        e[idx] = a, f[idx] = 0, ne[idx] = h[b], h[b] = idx ++ ;
    }
    
    bool bfs()
    {
        int hh = 0, tt = 0;
        memset(d, -1, sizeof d);
        q[0] = S, d[S] = 0, cur[S] = h[S];
        while (hh <= tt)
        {
            int t = q[hh ++ ];
            for (int i = h[t]; ~i; i = ne[i])
            {
                int ver = e[i];
                if (d[ver] == -1 && f[i])
                {
                    d[ver] = d[t] + 1;
                    cur[ver] = h[ver];
                    if (ver == T) return true;
                    q[ ++ tt] = ver;
                }
            }
        }
        return false;
    }
    
    int find(int u, int limit)
    {
        if (u == T) return limit;
        int flow = 0;
        for (int i = cur[u]; ~i && flow < limit; i = ne[i])
        {
            cur[u] = i;
            int ver = e[i];
            if (d[ver] == d[u] + 1 && f[i])
            {
                int t = find(ver, min(f[i], limit - flow));
                if (!t) d[ver] = -1;
                f[i] -= t, f[i ^ 1] += t, flow += t;
            }
        }
        return flow;
    }
    
    int dinic()
    {
        int r = 0, flow;
        while (bfs()) while (flow = find(S, INF)) r += flow;
        return r;
    }
    
    int main()
    {
        scanf("%d%d", &n, &m);
        S = 0, T = n + 1;
        memset(h, -1, sizeof h);
        for (int i = 0; i < m; i ++ )
        {
            int a, b, c, d;
            scanf("%d%d%d%d", &a, &b, &c, &d);
            add(a, b, c, d);
            A[a] -= c, A[b] += 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\n", f[i ^ 1] + l[i]);
        }
        return 0;
    }
    
  • 有源汇上下界可行流

    即使添加了源点 \(s\) 和汇点 \(t\),我们仍然可以通过从 \(t\) 到 \(s\) 添加一条下界为 0、上界为 \(+\infty\) 的边,将问题转化为无源汇上下界可行流问题,直接求解。此时,可行流的容量等于从 \(t\) 到 \(s\) 的边的容量。

    此时,原图的源点和汇点已视为普通点,而添加的超级源点和汇点分别为 \(s'\) 和 \(t'\)。可行流的容量就是从 \(t\) 到 \(s\) 的附加边的容量。

  • 有源汇上下界最大流

    在可行流的基础上,删除所有附加边后,使用原源汇节点求解一次最大流,再加上可行流的值即为最终答案。

    因为如果存在可行流,原网络一定是平衡的,当前网络同样平衡,满足平衡条件,因此是正确的。

    实际上,不必删除所有附加边,只需要删除 \(t\) 到 \(s\) 的附加边,因为这两个入度为 0 或出度为 0 的节点对最大流的计算没有影响。

    here
    #include <iostream>
    #include <cstring>
    #include <algorithm>
    
    using namespace std;
    
    const int N = 210, M = (N + 10000) * 2, INF = 1e8;
    
    int n, m, S, T;
    int h[N], e[M], f[M], ne[M], idx;
    int q[N], d[N], cur[N], A[N];
    
    void add(int a, int b, int c)
    {
    	e[idx] = b, f[idx] = c, ne[idx] = h[a], h[a] = idx ++ ;
    	e[idx] = a, f[idx] = 0, ne[idx] = h[b], h[b] = idx ++ ;
    }
    
    bool bfs()
    {
    	int hh = 0, tt = 0;
    	memset(d, -1, sizeof d);
    	q[0] = S, d[S] = 0, cur[S] = h[S];
    	while (hh <= tt)
    	{
    		int t = q[hh ++ ];
    		for (int i = h[t]; ~i; i = ne[i])
    		{
    			int ver = e[i];
    			if (d[ver] == -1 && f[i])
    			{
    				d[ver] = d[t] + 1;
    				cur[ver] = h[ver];
    				if (ver == T) return true;
    				q[ ++ tt] = ver;
    			}
    		}
    	}
    	return false;
    }
    
    int find(int u, int limit)
    {
    	if (u == T) return limit;
    	int flow = 0;
    	for (int i = cur[u]; ~i && flow < limit; i = ne[i])
    	{
    		cur[u] = i;
    		int ver = e[i];
    		if (d[ver] == d[u] + 1 && f[i])
    		{
    			int t = find(ver, min(f[i], limit - flow));
    			if (!t) d[ver] = -1;
    			f[i] -= t, f[i ^ 1] += t, flow += t;
    		}
    	}
    	return flow;
    }
    
    int dinic()
    {
    	int r = 0, flow;
    	while (bfs()) while (flow = find(S, INF)) r += flow;
    	return r;
    }
    
    int main()
    {
    	int s, t;
    	scanf("%d%d%d%d", &n, &m, &s, &t);
    	S = 0, T = n + 1;
    	memset(h, -1, sizeof h);
    	while (m -- )
    	{
    		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 = f[idx - 1];
    		S = s, T = t;
    		f[idx - 1] = f[idx - 2] = 0;
    		printf("%d\n", res + dinic());
    	}
    	return 0;
    }
    
  • 有源汇上下界最小流

    有源汇上下界最小流 思路基本相同。
    反向跑一边最大流,相当于流回去最大,那么就是最小流。
    答案 \(=\) 可行流 \(-\) 反向最大流

    点击查看代码
    #include <iostream>
    #include <cstring>
    #include <algorithm>
    
    using namespace std;
    
    const int N = 50010, M = (N + 125003) * 2, INF = 2147483647;
    
    int n, m, S, T;
    int h[N], e[M], f[M], ne[M], idx;
    int q[N], d[N], cur[N], A[N];
    
    void add(int a, int b, int c)
    {
    	e[idx] = b, f[idx] = c, ne[idx] = h[a], h[a] = idx ++ ;
    	e[idx] = a, f[idx] = 0, ne[idx] = h[b], h[b] = idx ++ ;
    }
    
    bool bfs()
    {
    	int hh = 0, tt = 0;
    	memset(d, -1, sizeof d);
    	q[0] = S, d[S] = 0, cur[S] = h[S];
    	while (hh <= tt)
    	{
    		int t = q[hh ++ ];
    		for (int i = h[t]; ~i; i = ne[i])
    		{
    			int ver = e[i];
    			if (d[ver] == -1 && f[i])
    			{
    				d[ver] = d[t] + 1;
    				cur[ver] = h[ver];
    				if (ver == T) return true;
    				q[ ++ tt] = ver;
    			}
    		}
    	}
    	return false;
    }
    
    int find(int u, int limit)
    {
    	if (u == T) return limit;
    	int flow = 0;
    	for (int i = cur[u]; ~i && flow < limit; i = ne[i])
    	{
    		cur[u] = i;
    		int ver = e[i];
    		if (d[ver] == d[u] + 1 && f[i])
    		{
    			int t = find(ver, min(f[i], limit - flow));
    			if (!t) d[ver] = -1;
    			f[i] -= t, f[i ^ 1] += t, flow += t;
    		}
    	}
    	return flow;
    }
    
    int dinic()
    {
    	int r = 0, flow;
    	while (bfs()) while (flow = find(S, INF)) r += flow;
    	return r;
    }
    
    int main()
    {
    	int s, t;
    	scanf("%d%d%d%d", &n, &m, &s, &t);
    	S = 0, T = n + 1;
    	memset(h, -1, sizeof h);
    	while (m -- )
    	{
    		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 = f[idx - 1];
    		S = t, T = s;
    		f[idx - 1] = f[idx - 2] = 0;
    		printf("%d\n", res - dinic());
    	}
    
    	return 0;
    }
    
  • 多源汇最大流

    • 建立一个超级源点 \(S\),向每个 \(s\) 连一条容量为 $+\infty $ 的边。
    • 建立一个超级汇点 \(T\),每个 \(t\) 向 \(T\) 连一条容量为 $+\infty $ 的边。

    \(S \to T\) 的最大流即为原图多源汇最大流。

    here
    #include <iostream>
    #include <cstring>
    #include <algorithm>
    
    using namespace std;
    
    const int N = 10010, M = (100000 + N) * 2, INF = 1e8;
    
    int n, m, S, T;
    int h[N], e[M], f[M], ne[M], idx;
    int q[N], d[N], cur[N];
    
    void add(int a, int b, int c)
    {
    	e[idx] = b, f[idx] = c, ne[idx] = h[a], h[a] = idx ++ ;
    	e[idx] = a, f[idx] = 0, ne[idx] = h[b], h[b] = idx ++ ;
    }
    
    bool bfs()
    {
    	int hh = 0, tt = 0;
    	memset(d, -1, sizeof d);
    	q[0] = S, d[S] = 0, cur[S] = h[S];
    	while (hh <= tt)
    	{
    		int t = q[hh ++ ];
    		for (int i = h[t]; ~i; i = ne[i])
    		{
    			int ver = e[i];
    			if (d[ver] == -1 && f[i])
    			{
    				d[ver] = d[t] + 1;
    				cur[ver] = h[ver];
    				if (ver == T) return true;
    				q[ ++ tt] = ver;
    			}
    		}
    	}
    	return false;
    }
    
    int find(int u, int limit)
    {
    	if (u == T) return limit;
    	int flow = 0;
    	for (int i = cur[u]; ~i && flow < limit; i = ne[i])
    	{
    		cur[u] = i;
    		int ver = e[i];
    		if (d[ver] == d[u] + 1 && f[i])
    		{
    			int t = find(ver, min(f[i], limit - flow));
    			if (!t) d[ver] = -1;
    			f[i] -= t, f[i ^ 1] += t, flow += t;
    		}
    	}
    	return flow;
    }
    
    int dinic()
    {
    	int r = 0, flow;
    	while (bfs()) while (flow = find(S, INF)) r += flow;
    	return r;
    }
    
    int main()
    {
    	int sc, tc;
    	scanf("%d%d%d%d", &n, &m, &sc, &tc);
    	S = 0, T = n + 1;
    	memset(h, -1, sizeof h);
    	while (sc -- )
    	{
    		int x;
    		scanf("%d", &x);
    		add(S, x, INF);
    	}
    	while (tc -- )
    	{
    		int x;
    		scanf("%d", &x);
    		add(x, T, INF);
    	}
    
    	while (m -- )
    	{
    		int a, b, c;
    		scanf("%d%d%d", &a, &b, &c);
    		add(a, b, c);
    	}
    
    	printf("%d\n", dinic());
    	return 0;
    }
    

标签:题型,idx,int,ne,add,include,hh,梳理,模板
From: https://www.cnblogs.com/fanrunze/p/18550276

相关文章

  • vue基础之3:模板语法、数据绑定
    欢迎来到“雪碧聊技术”CSDN博客!在这里,您将踏入一个专注于Java开发技术的知识殿堂。无论您是Java编程的初学者,还是具有一定经验的开发者,相信我的博客都能为您提供宝贵的学习资源和实用技巧。作为您的技术向导,我将不断探索Java的深邃世界,分享最新的技术动态、实战经验以及项目......
  • 判断正则二叉树———概念 + 实现模板 + 例题详解(简单易懂)
    判断正则二叉树递归判断概念正则二叉树(RegularBinaryTree):但每个节点要么有两个子节点,要么是叶子节点。实现思路1.设置递归终止条件(1)空节点(node==nullptr)————>returntrue;(2)只有一个子树(!root->left&&root->rig......
  • Halcon——使用Halcon模板匹配助手自动生成模板匹配代码
    1.找到模板助手模板助手的位置在菜单栏,助手——>打开新的Maching当出现下面这种弹窗时,就说明你已经成功找到Halcon模板匹配助手啦~2.模板匹配助手的操作流程read_image(Image,'D:/CStest/Halcon/MachineVision-main/CodeSet/test_image/1.png')(1)创建先读一张图片,这......
  • 洛谷题单指南-线段树-P3373 【模板】线段树 2
    原题链接:https://www.luogu.com.cn/problem/P3373题意解读:对于序列a[n],支持三种操作:1.对区间每个数乘上一个数2.对区间每个数加上一个数3.求区间和解题思路:由于支持乘、加两种区间修改操作,是线段树的另一种典型应用:多个懒标记显然,这里需要两个懒标记,mul表示对子节点区间每个......
  • 新手必看——ctf六大题型介绍及六大题型解析&举例解题
    CTF(CaptureTheFlag)介绍与六大题型解析一、什么是CTF?CTF(CaptureTheFlag),意为“夺旗赛”,是一种信息安全竞赛形式,广泛应用于网络安全领域。CTF竞赛通过模拟现实中的网络安全攻防战,让参赛者以攻防对抗的形式,利用各种信息安全技术进行解决一系列安全问题,最终获得“旗帜(Flag)”......
  • 【模板】exgcd
    #include<bits/stdc++.h>usingnamespacestd;#defineintlonglongconstintN=1e6+10;intT,a,b,c,d,x,y,num;intexgcd(inta,intb,int&x,int&y){ if(b==0){ x=1;y=0;returna; } intd=exgcd(b,a%b,x,y); intz=x;x=y;y=z-(a/b)*y......
  • 速领!人才盘点360度调查反馈模板-附下载链接
     ......
  • P3388 【模板】割点(割顶)
    【模板】割点(割顶)题目背景割点题目描述给出一个\(n\)个点,\(m\)条边的无向图,求图的割点。输入格式第一行输入两个正整数\(n,m\)。下面\(m\)行每行输入两个正整数\(x,y\)表示\(x\)到\(y\)有一条边。输出格式第一行输出割点个数。第二行按照节点编号从小到大输......
  • 高精度模板
    明天考恩欧挨批,但是我今天才学高精度:)随便敲敲#include<bits/stdc++.h>#defineintlonglongusingnamespacestd;constintmaxx=1e5+5;stringa,b;intans[maxx];inlinevoidadd(stringxx,stringyy){ memset(ans,0,sizeof(ans)); intlenx=xx.length(),leny=yy.leng......
  • 无监督模板辅助点云形状对应网络
    无监督模板辅助点云形状对应网络   无监督点云形状对应旨在建立源点云和目标点云之间的逐点对应关系。现有方法通过计算点云之间的逐点特征相似度直接获得对应关系。然而,非刚性物体具有很强的变形能力和不寻常的形状,因此直接在具有非常规形状的点云之间建立对应关系是一个长......