金牌导航-网络流初探

网络流初探

例题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\),那么需要有

\[x\times cnt_0-sum_0=x\times cnt_1-sum_1\\ \iff x\times(cnt_0-cnt_1)=sum_0-sum_1 \]

上式满足当且仅当 \(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;
}
posted @   Call_me_Eric  阅读(18)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 25岁的心里话
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
Live2D
欢迎阅读『金牌导航-网络流初探』
点击右上角即可分享
微信分享提示