willowwgx 2020-06-12
考虑最小割数学形式
考虑一个点在 \(S\) 集合里是 0 在 \(T\) 集合里是 1
那么一个点是 1 时,它只有在零里才有贡献连边 \(x \to T\) 否则连边 \(S \to x\)
当两个点时朋友时,他们在不同的集合里才会有贡献,连边 \(x \to y\), \(y \to x\)
#include <bits/stdc++.h> using namespace std; #define rg register #define rep(i ,a, b) for(int i = a; i <= b; ++i) #define per(i, a, b) for(int i = a; i >= b; --i) #define I inline const int N = 305, M = (N * N) << 5; int head[N], ver[M], nxt[M], flow[M], tot = 1; I void add(int x, int y, int f){ ver[++tot] = y; nxt[tot] = head[x]; head[x] = tot; flow[tot] = f; } I void adds(int x, int y, int f){ add(x, y, f); add(y, x, 0); } int S, T, cur[N], dis[N]; I int bfs(){ memset(dis, 0, sizeof dis); queue<int> q; q.push(S); dis[S] = 1; while(!q.empty()){ int x = q.front(); q.pop(); cur[x] = head[x]; for(int i = head[x]; i; i = nxt[i]){ int y = ver[i]; if(flow[i] && !dis[y]){ dis[y] = dis[x] + 1; q.push(y); } } } return dis[T]; } int dfs(int x, int f){ if(x == T) return f; int used = 0; for(int &i = cur[x]; i; i = nxt[i]){ int y = ver[i]; if(flow[i] && dis[y] == dis[x] + 1){ int w = dfs(y, min(flow[i], f - used)); if(w){ flow[i] -= w; flow[i ^ 1] += w; used += w; if(used == f) return f; } } } if(!used) dis[x] = false; return used; } I int dinic(){ int res = 0; while(bfs()) res += dfs(S, 0x3f3f3f3f); return res; } int n, m; signed main(){ ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); cin >> n >> m; int x, y; S = 0, T = n + 1; rep(i, 1, n){ cin >> x; if(x){ adds(i, T, 1); }else adds(S, i, 1); } rep(i, 1, m){ cin >> x >> y; adds(x, y, 1); adds(y, x, 1); } cout << dinic() << endl; return 0; }
Set,List,Map的区别和功能到底是怎样的?其实它是与数组区分开来的。与数学中的集合最接近,两者都不包含重复元素。它的有些实现类能对集合中的键对象进行排序。切记在用到MAP时一定需要传入两个参数