[BZOJ1458][luogu 4311] 士兵占领 网络流建模

那天听 dyx d y x 讲课还是懵逼了半天 结果发现自己根本没怎么做过网络流, 板子都不会打了 qwq q w q
还是一点点从他的课件里刷起来把

这个题我们首先可以换种思维方法 在有限制的情况下放最少的士兵
可以把所有士兵放上去之后 在有限制的情况下删最多的士兵
这样子建立一个超级源 S S 向每一行连一条流量为当前点数减去限制的边, 代表这一行最多能删几个点 每一列向超级汇T也连一条这样的边, 那么对于每一个没有障碍的点 (x,y) ( x , y ) 连一条行 x x 到列y的边 然后跑最大流即可
最后的答案 ans=nmkMaxflow a n s = n ∗ m − k − M a x f l o w

BZOJ上是权限题就放洛谷的吧

Codes

#include<bits/stdc++.h>
#define For(i, a, b) for(register int i = a; i <= b; ++ i)
#define go(x, i) for(register int i = head[x]; i; i = nxt[i])
#define inf (0x3f3f3f3f)

using namespace std;
const int maxn = 100 + 10;
int n, m, k, H[maxn], L[maxn], a[maxn][maxn];
int SumH[maxn], SumL[maxn];

namespace Max_Flow {
    const int maxm = maxn * maxn * 2;
    int to[maxm << 1], head[maxm], nxt[maxm << 1], w[maxm << 1], e = 1;
    int dis[maxm], S, T;

    void add(int x, int y, int z) {
        to[++ e] = y;
        nxt[e] = head[x];
        head[x] = e;
        w[e] = z;
    }

    bool bfs() {
        queue<int> q;
        For(i, 1, T) dis[i] = 0;
        dis[S] = 1, q.push(S);
        while(!q.empty()) {
            int k = q.front(); q.pop();
            go(k, i)
                if(!dis[to[i]] && w[i]) {
                    dis[to[i]] = dis[k] + 1;
                    q.push(to[i]);
                }
        }
        return dis[T];
    }

    int dfs(int x, int flow) {
        if(x == T || !flow)
            return flow;
        int used = 0;
        go(x, i)
            if(dis[to[i]] == dis[x] + 1 && w[i]) {
                int di = dfs(to[i], min(flow, w[i]));
                w[i] -= di, w[i ^ 1] += di;
                flow -= di, used += di;
                if(!flow) break;
            }
        return used;
    }

    int solve()
    {
        int res = 0; S = n + m + 1, T = S + 1;
        For(i, 1, n) add(S, i, SumH[i] - H[i]), add(i, S, 0);
        For(i, 1, m) add(i + n, T, SumL[i] - L[i]), add(T, i + n, 0);
        For(i, 1, n)
            For(j, 1, m)
                if(!a[i][j]) 
                    add(i, j + n, 1), add(j + n, i, 0);
        while(bfs()) 
            while(true) {
                int flow = dfs(S, inf);
                res += flow;
                if(!flow) break;
            }
        return res;
    }
}

int main() {
#ifndef ONLINE_JUDGE
    freopen("1458.in", "r", stdin);
    freopen("1458.out", "w", stdout);
#endif
    int x, y, flag = 0;
    cin >> n >> m >> k;
    For(i, 1, n) scanf("%d", &H[i]), SumH[i] = m;
    For(i, 1, m) scanf("%d", &L[i]), SumL[i] = n;
    For(i, 1, k) {
        scanf("%d%d", &x, &y);
        -- SumH[x], -- SumL[y];
        if(SumH[x] < H[x] || SumL[y] < L[y]) 
            flag = 1;
        a[x][y] = true;
    }
    if(flag) {
        puts("JIONG!");
        return 0;
    }
    printf("%d\n", n * m - k - Max_Flow::solve());
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值