金牌导航-网络流初探
网络流初探
例题A题解
网络流板子,不会自己学
例题A代码
#include<bits/stdc++.h> #define int long long using namespace std; inline int read(){ int x = 0, f =1;char ch = getchar(); while(ch < '0' || ch > '9'){if(ch == '-') f = -1;ch = getchar();} while(ch >= '0' && ch <= '9'){x = (x << 1) + (x << 3) + (ch ^ 48);ch = getchar();} return x * f; } const int maxn = 2e2 + 10,maxm = 5e3 + 10, INF = 0x3f3f3f3f3f3f3f3f; int n, m; int head[maxn], tot = 1; struct edge{ int to, nexte, cap, flow; edge(int to = 0,int ne = 0,int ca = 0,int fl = 0):to(to),nexte(ne),cap(ca),flow(fl){} }e[maxm * 2]; void add(int u,int v,int cap){e[++tot] = edge(v,head[u],cap);head[u] = tot;} void addd(int u,int v,int cap){add(u,v,cap);add(v, u, 0);} int dep[maxn], cur[maxn]; int dfs(int u,int flow,int T){ // printf("%lld\n",u); if(u == T)return flow; int rest = 0, tmp = 0; for(int i = cur[u];i && flow;i = e[i].nexte){ cur[u] = i; int v = e[i].to; if(e[i].cap - e[i].flow > 0 && (dep[v] == dep[u] + 1)){ tmp = dfs(v,min(flow,e[i].cap - e[i].flow),T); if(tmp == 0)dep[v] = INF; e[i].flow += tmp; e[i ^ 1].flow -= tmp; flow -= tmp;rest += tmp; if(!flow)return rest; } } return rest; } bool bfs(int S,int T){ queue<int> que; for(int i = 1;i <= n;i++){dep[i] = INF;cur[i] = 0;} que.push(S);dep[S] = 1; cur[S] = head[S]; while(!que.empty()){ int u = que.front(); que.pop(); for(int i = head[u];i;i = e[i].nexte){ int v = e[i].to; if(e[i].cap - e[i].flow > 0 && dep[v] == INF){ que.push(v); cur[v] = head[v]; dep[v] = dep[u] + 1; if(v == T)return 1; } } } return 0; } int Dinic(int S,int T){ int mxflow = 0; while(bfs(S,T)){mxflow += dfs(S,INF,T);} return mxflow; } signed main(){ int S, T; m = read();n = read();S = 1;T = n;int u, v, w; for(int i = 1;i <= m;i++){ u = read(); v = read(); w =read(); addd(u, v, w); } printf("%lld\n",Dinic(S,T)); return 0; }
例题B题解
从源点向每个猪圈连边,每个人向汇点连边。然后对于每个人所能打开的猪圈,如果在此之前没有被其他人连过,就让这个猪圈连向这个人,否则让这个人连向之前那个人。
例题B代码
#include<bits/stdc++.h> using namespace std; inline int read(){ int x = 0, f = 1;char ch = getchar(); while(ch < '0' || ch > '9'){if(ch == '-') f = -1;ch = getchar();} while(ch >= '0' && ch <= '9'){x = (x << 1) + (x << 3) + (ch ^ 48);ch = getchar();} return x * f; } const int maxn = 2e3 + 10, maxm = 5e5 + 10, INF = 0x3f3f3f3f; int n, m; int head[maxn], tot = 1; struct edge{ int to, nexte, cap, flow; edge(int to = 0,int ne = 0,int ca = 0,int fl = 0):to(to),nexte(ne),cap(ca),flow(fl){} }e[maxm * 2]; void add(int u,int v,int cap){e[++tot] = edge(v,head[u],cap);head[u] = tot;} void addd(int u,int v,int cap){add(u,v,cap);add(v, u, 0);} int dep[maxn], cur[maxn]; int dfs(int u,int flow,int T){ // printf("%lld\n",u); if(u == T)return flow; int rest = 0, tmp = 0; for(int i = cur[u];i && flow;i = e[i].nexte){ cur[u] = i; int v = e[i].to; if(e[i].cap - e[i].flow > 0 && (dep[v] == dep[u] + 1)){ tmp = dfs(v,min(flow,e[i].cap - e[i].flow),T); if(tmp == 0)dep[v] = INF; e[i].flow += tmp; e[i ^ 1].flow -= tmp; flow -= tmp;rest += tmp; if(!flow)return rest; } } return rest; } bool bfs(int S,int T){ queue<int> que; for(int i = 1;i <= T;i++){dep[i] = INF;cur[i] = 0;} que.push(S);dep[S] = 1; cur[S] = head[S]; while(!que.empty()){ int u = que.front(); que.pop(); for(int i = head[u];i;i = e[i].nexte){ int v = e[i].to; if(e[i].cap - e[i].flow > 0 && dep[v] == INF){ que.push(v); cur[v] = head[v]; dep[v] = dep[u] + 1; if(v == T)return 1; } } } return 0; } int Dinic(int S,int T){ int mxflow = 0; while(bfs(S,T)){mxflow += dfs(S,INF,T);} return mxflow; } int match[maxn]; signed main(){ m = read(); n = read();int S = n + m + 1, T = n + m + 2; for(int i = 1;i <= m;i++)addd(S,i,read()); for(int i = 1;i <= n;i++){ for(int j = read();j;j--){ int x = read(); if(!match[x])addd(x,i + m,INF); else addd(match[x] + m,i + m,INF); match[x] = i; } addd(i + m,T,read()); } printf("%d\n",Dinic(S,T)); return 0; }
例题C题解
不太好想的一道题。
首先有一个基本思路,尝试黑白染色。
不难发现,黑白点总是同时增加,也就是说,增加的数量相同
然后发现一个必要条件:如果设黑点的总数是 \(cnt_0\),权值和是 \(sum_0\),白点同理。如果设最终全局权值为 \(x\),那么需要有
上式满足当且仅当 \(cnt_0=cnt_1\) 或者 \(cnt_0\neq cnt_1,x=\frac{sum_0-sum_1}{cnt_0-cnt_1}\)。
分类讨论:
- \(cnt_0\neq cnt_1\):\(check\) 一下算出的 \(x\) 即可。
- \(cnt_0=cnt_1\):出现这种情况当且仅当 \(2 | (n\times m)\)。这个时候不难发现,如果 \(x\) 能够达成,那么 \(x+1\) 就一定能够达成(一黑一白在加一遍覆盖整体即可),但是 \(x-1\) 就不一定了。因此二分答案进行 \(check\)。
然后讲一下怎么 \(check\)。
在确定了最终局面的时候,对每个黑点从原点连接 \(mid-val_{i,j}\) 容量的边,对每个白点向汇点连接 \(mid - val_{i,j}\) 容量的边,黑点连向白点。然后跑最大流,判断是否全部满流即可。
需要注意的是,答案不一定在 \(10^9\) 范围内,二分答案的时候可以开到 \(10^{16}\)
例题C代码
#include<bits/stdc++.h> #define int long long using namespace std; inline int read(){ int x = 0, f = 1;char ch = getchar(); while(ch < '0' || ch > '9'){if(ch == '-') f = -1;ch = getchar();} while(ch >= '0' && ch <= '9'){x = (x << 1) + (x << 3) + (ch ^ 48);ch = getchar();} return x * f; } const int maxn = 2e3 + 10, maxm = 5e5 + 10, INF = 0x3f3f3f3f3f3f3f3f; int n, m; int head[maxn], tot = 1; struct edge{ int to, nexte, cap, flow; edge(int to = 0,int ne = 0,int ca = 0,int fl = 0):to(to),nexte(ne),cap(ca),flow(fl){} }e[maxm * 2]; void add(int u,int v,int cap){e[++tot] = edge(v,head[u],cap);head[u] = tot;} void addd(int u,int v,int cap){add(u,v,cap);add(v, u, 0);} int dep[maxn], cur[maxn]; int dfs(int u,int flow,int T){ // printf("%lld\n",u); if(u == T)return flow; int rest = 0, tmp = 0; for(int i = cur[u];i && flow;i = e[i].nexte){ cur[u] = i; int v = e[i].to; if(e[i].cap - e[i].flow > 0 && (dep[v] == dep[u] + 1)){ tmp = dfs(v,min(flow,e[i].cap - e[i].flow),T); if(tmp == 0)dep[v] = INF; e[i].flow += tmp; e[i ^ 1].flow -= tmp; flow -= tmp;rest += tmp; if(!flow)return rest; } } return rest; } bool bfs(int S,int T){ queue<int> que; for(int i = 1;i <= T;i++){dep[i] = INF;cur[i] = 0;} que.push(S);dep[S] = 1; cur[S] = head[S]; while(!que.empty()){ int u = que.front(); que.pop(); for(int i = head[u];i;i = e[i].nexte){ int v = e[i].to; if(e[i].cap - e[i].flow > 0 && dep[v] == INF){ que.push(v); cur[v] = head[v]; dep[v] = dep[u] + 1; if(v == T)return 1; } } } return 0; } int Dinic(int S,int T){ int mxflow = 0; while(bfs(S,T)){mxflow += dfs(S,INF,T);} return mxflow; } int a[100][100]; const int dx[4] = { 0, 0,-1, 1}; const int dy[4] = {-1, 1, 0, 0}; int check(int x){ tot = 1;memset(head,0,sizeof(head)); auto getid = [&](int x,int y){return (x - 1) * m + y;}; int sum = 0, S = getid(n, m) + 1, T = getid(n, m) + 2; for(int i = 1;i <= n;i++){ for(int j = 1;j <= m;j++){ if(x < a[i][j])return -1; sum += x - a[i][j]; if((i + j) & 1){ addd(S,getid(i, j),x - a[i][j]); for(int k = 0;k < 4;k++){ int nx = i + dx[k], ny = j + dy[k]; if(nx < 1 || nx > n || ny < 1 || ny > m)continue; addd(getid(i, j),getid(nx, ny),INF); } } else {addd(getid(i, j),T,x - a[i][j]);} } } int tmp = Dinic(S,T) * 2; return tmp == sum ? sum : -1; } void solve(){ n = read(); m = read(); int sum[2] = {0}, cnt[2] = {0}, mx = 0; for(int i = 1;i <= n;i++) for(int j = 1;j <= m;j++){ mx = max(mx,a[i][j] = read()); sum[(i + j) & 1] += a[i][j]; cnt[(i + j) & 1] ++; } if(cnt[0] != cnt[1]){ int tmp = check((sum[0] - sum[1]) / (cnt[0] - cnt[1])); printf("%lld\n",tmp != -1 ? tmp / 2 : -1); return; } if(sum[0] != sum[1]){puts("-1");return;} int l = mx, r = 1e16, mid, ans = -1; while(l <= r){ mid = l + r >> 1; int tmp = check(mid); if(tmp != -1){r = mid - 1,ans = tmp;} else l = mid + 1; } printf("%lld\n",ans != -1 ? ans / 2 : -1); // puts("11111111111111"); } signed main(){ int test = read(); while(test--)solve(); return 0; }
例题D题解
如果跑多源汇最大流,那么可能出现一种情况就是 \(a_1\to b_2\),\(a_2\to b_1\) 然后导致最终答案正好满足要求的情况。
怎么办呢?
跑一遍 \(S\to a_1,S\to b_1,a_2\to T,b_2\to T\),再跑一遍 \(S\to a_1,S\to b_2,a_2\to T,b_1\to T\) 就好了。
为什么是对的?换句话说,可不可能出现一种情况,使得第一次跑的时候有一条增广路 \(a_1\to b_2\),且第二次跑的时候也有一条增广路 \(a_1\to b_1\)?
简陋证明:不难发现,如果出现这样的增广路,因为是无向图,那么一定有一条增广路 \(b_1\to b_2\),那么这条路也能贡献到真正答案中,因此两次匹配就是对的。
例题D代码
#include<bits/stdc++.h> using namespace std; inline int read(){ int x = 0, f = 1;char ch = getchar(); while(ch < '0' || ch > '9'){if(ch == '-') f = -1;ch = getchar();} while(ch >= '0' && ch <= '9'){x = (x << 1) + (x << 3) + (ch ^ 48);ch = getchar();} return x * f; } const int maxn = 2e3 + 10, maxm = 5e5 + 10, INF = 0x3f3f3f3f; int n, m; int head[maxn], tot = 1; struct edge{ int to, nexte, cap, flow; edge(int to = 0,int ne = 0,int ca = 0,int fl = 0):to(to),nexte(ne),cap(ca),flow(fl){} }e[maxm * 2]; void add(int u,int v,int cap){e[++tot] = edge(v,head[u],cap);head[u] = tot;} void addd(int u,int v,int cap){add(u,v,cap);add(v, u, 0);} int dep[maxn], cur[maxn]; int dfs(int u,int flow,int T){ // printf("%lld\n",u); if(u == T)return flow; int rest = 0, tmp = 0; for(int i = cur[u];i && flow;i = e[i].nexte){ cur[u] = i; int v = e[i].to; if(e[i].cap - e[i].flow > 0 && (dep[v] == dep[u] + 1)){ tmp = dfs(v,min(flow,e[i].cap - e[i].flow),T); if(tmp == 0)dep[v] = INF; e[i].flow += tmp; e[i ^ 1].flow -= tmp; flow -= tmp;rest += tmp; if(!flow)return rest; } } return rest; } bool bfs(int S,int T){ queue<int> que; for(int i = 1;i <= T;i++){dep[i] = INF;cur[i] = 0;} que.push(S);dep[S] = 1; cur[S] = head[S]; while(!que.empty()){ int u = que.front(); que.pop(); for(int i = head[u];i;i = e[i].nexte){ int v = e[i].to; if(e[i].cap - e[i].flow > 0 && dep[v] == INF){ que.push(v); cur[v] = head[v]; dep[v] = dep[u] + 1; if(v == T)return 1; } } } return 0; } int Dinic(int S,int T){ int mxflow = 0; while(bfs(S,T)){mxflow += dfs(S,INF,T);} return mxflow; } char ch[100][100]; int a[4], b[4]; signed main(){ while(scanf("%d",&n) != EOF){ memset(head,0,sizeof(head));tot = 1; for(int i = 1;i <= 3;i++)a[i] = read() + (i <= 2); for(int i = 1;i <= 3;i++)b[i] = read() + (i <= 2); int S = n + 1, T = n + 2; for(int i = 1;i <= n;i++){ scanf("%s",ch[i] + 1); for(int j = 1;j <= n;j++){ if(ch[i][j] == 'N')addd(i, j, INF); if(ch[i][j] == 'O')addd(i, j, 1); } } addd(S,a[1],a[3]);addd(a[2],T,a[3]); addd(S,b[1],b[3]);addd(b[2],T,b[3]); int ans1 = Dinic(S,T); tot = 1;memset(head,0,sizeof(head)); for(int i = 1;i <= n;i++){ for(int j = 1;j <= n;j++){ if(ch[i][j] == 'N')addd(i, j, INF); if(ch[i][j] == 'O')addd(i, j, 1); } } addd(S,a[1],a[3]);addd(a[2],T,a[3]); addd(S,b[2],b[3]);addd(b[1],T,b[3]); int ans2 = Dinic(S,T); if(ans1 == a[3] + b[3] && ans2 == a[3] + b[3]) puts("Yes"); else puts("No"); } return 0; }
例题E题解
这个老套路,对于每个支柱,拆成两个点,一个入点,一个出点,然后容量就是次数,然后就没了。
例题E代码
#include<bits/stdc++.h> using namespace std; inline int read(){ int x = 0,f = 1;char ch = getchar(); while(ch < '0' || ch > '9'){if(ch == '-') f = -1; ch = getchar();} while(ch >= '0' && ch <= '9'){x = (x << 1) + (x << 3) + ch - '0'; ch = getchar();} return x * f; } const int maxn = 21 * 21 * 3, INF = 0x3f3f3f3f; const int dx[60] = {-1,0,0,1,-2,-1,-1,0,0,1,1,2,-3,-2,-2,-1,-1,0,0,1,1,2,2,3,-4,-3,-3,-2,-2,-1,-1,0,0,1,1,2,2,3,3,4}; const int dy[60] = {0,-1,1,0,0,1,-1,2,-2,1,-1,0,0,1,-1,2,-2,3,-3,2,-2,1,-1,0,0,-1,1,-2,2,-3,3,-4,4,-3,3,-2,2,-1,1,0}; int head[maxn], cur[maxn], tot = 1; struct edge{ int to, nexte, flow; edge(int t = 0,int n = 0,int f = 0):to(t),nexte(n),flow(f){} }e[maxn * maxn]; void add(int u,int v,int flow){e[++tot] = edge(v,head[u],flow); head[u] = tot;} void addd(int u,int v,int flow){add(u,v,flow);add(v,u,0);} int n, m, d, S, T; int dep[maxn]; int dfs(int u,int flow){ // printf("%lld\n",u); if(u == T)return flow; int rest = 0, tmp = 0; for(int i = cur[u];i && flow;i = e[i].nexte){ cur[u] = i; int v = e[i].to; if(e[i].flow > 0 && (dep[v] == dep[u] + 1)){ tmp = dfs(v,min(flow,e[i].flow)); if(tmp == 0)dep[v] = INF; e[i].flow -= tmp; e[i ^ 1].flow += tmp; flow -= tmp; rest += tmp; } } return rest; } bool bfs(){ queue<int> que; for(int i = 1;i < maxn;i++){dep[i] = INF;cur[i] = 0;} que.push(S);dep[S] = 1; cur[S] = head[S]; while(!que.empty()){ int u = que.front(); que.pop(); for(int i = head[u];i;i = e[i].nexte){ int v = e[i].to; if(e[i].flow > 0 && dep[v] == INF){ que.push(v); cur[v] = head[v]; dep[v] = dep[u] + 1; if(v == T)return 1; } } } return 0; } int Dinic(){ int maxflow = 0; while(bfs()){maxflow += dfs(S,INF);} return maxflow; } char str[22]; int times[22][22], sum = 0; inline int idin(int x,int y){return x * m + y + 1;} inline int idout(int x,int y){return x * m + y + 1 + n * m;} bool reach(int x1,int y1,int x2,int y2,int limit){ return (x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2) <= limit * limit; } bool exist(int x,int y){return !(x <= 0 || x > n || y <= 0 || y > m); } void addedge(int x,int y,int dist){ if(x - dist <= 0 || x + dist > n || y - dist <= 0 || y + dist > m){addd(idout(x,y),T,INF);return;} for(int i = -dist;i <= dist;i++){ for(int j = -dist;j <= dist;j++){ if(!i && !j)continue; if(!exist(x + i,y + j) || !times[x + i][y + j])continue; if(reach(x,y,x + i,y + j,dist)){ addd(idout(x,y),idin(x + i,y + j),INF); } } } return; } int x[maxn],y[maxn]; void init(){ tot = 1;sum = 0;n = read();m = read();d = read(); for(int i = 1;i <= n;i++){ scanf("%s",str + 1); // if(i == 1) m = strlen(str + 1); for(int j = 1;j <= m;j++){ times[i][j] = str[j] - '0'; if(times[i][j])addd(idin(i,j),idout(i,j),times[i][j]); } } S = maxn - 2;T = maxn - 1; for(int i = 1;i <= n;i++){ scanf("%s",str + 1); for(int j = 1;j <= m;j++){ if(times[i][j]){ addedge(i,j,d); if(str[j] == 'L'){ sum++; x[sum] = i;y[sum] = j; addd(S,idin(i,j),1); } } } } for(int i = 1;i <= sum;i++){ for(int j = 1;j <= sum;j++){ if(i != j && reach(x[i],y[i],x[j],y[j],d)){ addd(idout(x[i],y[i]),idin(x[j],y[j]),INF); } } } } void getans(){ int ans = Dinic(); printf("%d\n",sum - ans); } signed main(){ //int test = read(); //while(test--){ init(); getans(); //} return 0; }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 25岁的心里话
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现