BZOJ2150 部落战争 【带上下界最小流】

题目链接

BZOJ2150

题解

复习:
带上下界网络流两种写法:

  1. 不建\(T->S\)\(INF\)的边,即不考虑源汇点,先求出此时超级源汇的最大流,即无源汇下最大的自我调整,再加入该边,求超级源汇最大流增加的流量
  2. 先求出【或观察出】\(S->T\)的最大流,记为\(tot\),然后撤销流量,再建立\(T->S\),求出超级源汇最大流\(f\),答案为\(tot - f\)

两者本质一样,但后者在\(S->T\)最大流确定的情况下,可以增加效率

#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<cstdio>
#include<vector>
#include<queue>
#include<cmath>
#include<map>
#define LL long long int
#define REP(i,n) for (int i = 1; i <= (n); i++)
#define Redge(u) for (int k = h[u],to; k; k = ed[k].nxt)
#define cls(s,v) memset(s,v,sizeof(s))
#define mp(a,b) make_pair<int,int>(a,b)
#define cp pair<int,int>
using namespace std;
const int maxn = 5005,maxm = 3000005,INF = 0x3f3f3f3f;
inline int read(){
    int out = 0,flag = 1; char c = getchar();
    while (c < 48 || c > 57){if (c == '-') flag = 0; c = getchar();}
    while (c >= 48 && c <= 57){out = (out << 1) + (out << 3) + c - 48; c = getchar();}
    return flag ? out : -out;
}
char sq[55];
int X[] = {1,1},Y[] = {-1,1};
int N,M,R,C,id[55][55],tot;
int S,T,q[maxn],head,tail,cur[maxn];
int now,used[maxn],vis[maxn],d[maxn];
int h[maxn],ne = 1;
struct EDGE{int to,nxt,f;}ed[maxm];
inline void build(int u,int v,int f){
    ed[++ne] = (EDGE){v,h[u],f}; h[u] = ne;
    ed[++ne] = (EDGE){u,h[v],0}; h[v] = ne;
}
bool bfs(int S,int T){
    q[head = tail = 0] = S; vis[S] = ++now;
    int u;
    while (head <= tail){
        u = q[head++];
        Redge(u) if (ed[k].f && vis[to = ed[k].to] != now){
            d[to] = d[u] + 1;
            vis[to] = now;
            q[++tail] = to;
            if (to == T) return true;
        }
    }
    return false;
}
int dfs(int u,int minf,int T){
    if (u == T || !minf) return minf;
    int f,flow = 0,to;
    if (used[u] != now) used[u] = now,cur[u] = h[u];
    for (int& k = cur[u]; k; k = ed[k].nxt)
        if (vis[to = ed[k].to] == now && d[to] == d[u] + 1 && (f = dfs(to,min(minf,ed[k].f),T))){
            ed[k].f -= f; ed[k ^ 1].f += f;
            flow += f; minf -= f;
            if (!minf) break;
        }
    return flow;
}
int main(){
    N = read(); M = read(); R = read(); C = read();
    REP(i,N){
        scanf("%s",sq + 1);
        REP(j,M) if (sq[j] == '.') id[i][j] = ++tot;
    }
    S = (tot << 1) + 1; T = S + 1;
    REP(i,N) REP(j,M){
        if (!id[i][j]) continue;
        int x,y,u = id[i][j];
        build(S,u + tot,1); build(u,T,1);
        for (int k = 0; k < 2; k++){
            x = i + X[k] * R;
            y = j + Y[k] * C;
            if (x < 1 || y < 1 || x > N || y > M || !id[x][y]) continue;
            build(u + tot,id[x][y],1);
        }
        if (R != C){
            for (int k = 0; k < 2; k++){
                x = i + X[k] * C;
                y = j + Y[k] * R;
                if (x < 1 || y < 1 || x > N || y > M || !id[x][y]) continue;
                build(u + tot,id[x][y],1);
            }
        }
    }
    int ans = tot;
    while (bfs(S,T)) ans -= dfs(S,INF,T);
    printf("%d\n",ans);
    return 0;
}

转载于:https://www.cnblogs.com/Mychael/p/9304882.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值