bzoj1458 士兵占领

Description

有一个 M×N M × N 的棋盘,有的格子是障碍。现在你要选择一些格子来放置一些士兵,一个格子里最多可以放置一个士兵,障碍格里不能放置士兵。我们称这些士兵占领了整个棋盘当满足第 i i 行至少放置了 Li 个士兵, 第j列至少放置了 Cj C j 个士兵。现在你的任务是要求使用最少个数的士兵来占领整个棋盘。

Input

第一行两个数 M,N,K M , N , K 分别表示棋盘的行数,列数以及障碍的个数。 第二行有 M M 个数表示 Li 。 第三行有 N N 个数表示 Ci 。 接下来有 K K 行,每行两个数 X,Y 表示 (X,Y) ( X , Y ) 这个格子是障碍。

Output

输出一个数表示最少需要使用的士兵个数。如果无论放置多少个士兵都没有办法占领整个棋盘,输出“JIONG!” (不含引号)

Sample Input

4 4 4
1 1 1 1
0 1 0 3
1 4
2 2
3 3
4 3

Sample Output

4

HINT

M,N100,0KM×N M , N ≤ 100 , 0 ≤ K ≤ M × N

Solution

azi只会做傻逼题

azi为什么傻逼题都要交这么多次啊

最大流

  • 最少使用多少 最多不用多少
  • num1[i] n u m 1 [ i ] 表示第 i i 行有多少格子没有障碍
  • num2[i] 表示第 i i 列有多少格子没有障碍
  • S 往每一行连边,流量为 num1[i]L[i] n u m 1 [ i ] − L [ i ]
  • 每一列往 T T 连边,流量为 num2[i]C[i]
  • 若第 i i 行第 j 列没有障碍,则第 i i 行往第 j 列连边,流量为 1 1 <script type="math/tex" id="MathJax-Element-55">1</script>
#include<bits/stdc++.h>
using namespace std;

#define N 1001
#define INF (1e9)
#define rep(i, a, b) for (int i = a; i <= b; i++)

inline int read() {
    int x = 0, flag = 1; char ch = getchar(); while (!isdigit(ch)) { if (!(ch ^ '-')) flag = -1; ch = getchar(); }
    while (isdigit(ch)) x = (x << 1) + (x << 3) + ch - '0', ch = getchar(); return x * flag;
}

struct edge { int v, c, next; }e[50001];
int head[5001], tot = 1;
int n, m, k, S, T, sum;
bool a[N][N];
int L[N], C[N], num1[N], num2[N];
int dep[N], q[N];

inline void add(int u, int v, int c) {
    e[++tot] = edge{ v, c, head[u] }; head[u] = tot;
    e[++tot] = edge{ u, 0, head[v] }; head[v] = tot;
}

inline bool bfs() {
    int l = 1, r = 1;
    memset(dep, 0, sizeof dep); q[r] = S, dep[S] = 1;
    while (l <= r) {
        int u = q[l++];
        for (int i = head[u]; i; i = e[i].next) {
            int v = e[i].v, c = e[i].c;
            if (c > 0 && !dep[v]) dep[v] = dep[u] + 1, q[++r] = v;
        }
    }
    return (bool)dep[T];
}

int dfs(int u, int dis) {
    if (u == T || dis <= 0) return dis;
    for (int i = head[u]; i; i = e[i].next) {
        int v = e[i].v, c = e[i].c;
        if (dep[v] != dep[u] + 1 || c <= 0) continue;
        int d = dfs(v, min(dis, c));
        if (d > 0) { e[i].c -= d, e[i ^ 1].c += d; return d; }
    }
    return 0;
}

int main() {
    scanf("%d%d%d", &n, &m, &k); T = n + m + 1;
    rep(i, 1, n) L[i] = read(), num1[i] = m;
    rep(i, 1, m) C[i] = read(), num2[i] = n;
    while (k--) {
        int x = read(), y = read();
        a[x][y] = 1; num1[x]--, num2[y]--;
    }
    rep(i, 1, n) if (num1[i] >= L[i]) add(S, i, num1[i] - L[i]), sum += num1[i]; else { puts("JIONG!"); return 0; }
    rep(i, 1, m) if (num2[i] >= C[i]) add(n + i, T, num2[i] - C[i]); else { puts("JIONG!"); return 0; }
    rep(i, 1, n) rep(j, 1, m) if (!a[i][j]) add(i, n + j, 1);
    int ans = 0; while (bfs()) ans += dfs(S, INF);
    cout << sum - ans;
    return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值