金牌导航-网络流初探

网络流初探

例题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 @ 2023-12-11 14:24  Call_me_Eric  阅读(54)  评论(0)    收藏  举报
Live2D