题目大意:有一个$n imes m$的棋盘,有的格子是障碍。要选择一些格子来放置士兵,一个空格子里可放一个士兵。希望第$i$行至少放置了$l_i$个士兵, 第$j$列至少放置了$c_j$个士兵。要求使用最少个数的士兵。输出个数。
题解:先考虑无解,只要所有能放士兵的地方都放上了士兵,仍然存在行或列不满足限制条件,即为无解。
逆向思考,把求使用最少的士兵转化为,放满士兵后,最多能删掉多少个士兵。网络流。
给每一行每一列分别建一个点,对于所有非障碍坐标$(x,y)$,从$x$行向$y$列连一条容量为$1$的边,表示可以删除一个士兵。
源点向每一行连边,容量为这一行能删除的士兵个数的最大值(即这一行可以放得士兵数减去这一行需要的士兵数)。
列也是这样。跑最大流,每一个单位的流量表示删除一个士兵,从总士兵个数中删除即可
卡点:无
C++ Code:
#include <cstdio> #include <cstring> #define maxn 111 using namespace std; const int inf = 0x3f3f3f3f; int n, m, k, sum; int l[maxn], c[maxn]; int L[maxn], C[maxn]; bool v[maxn][maxn]; int head[maxn << 1], cnt = 2; struct Edge { int to, nxt, w; } e[maxn * maxn << 1]; void add(int a, int b, int c) { e[cnt] = (Edge) {b, head[a], c}; head[a] = cnt; e[cnt ^ 1] = (Edge) {a, head[b], 0}; head[b] = cnt ^ 1; cnt += 2; } inline int min(int a, int b) {return a < b ? a : b;} int st, ed; int d[maxn << 1]; int q[maxn << 1], h, t; inline bool bfs() { memset(d, 0, sizeof d); d[q[h = t = 0] =st] = 1; while (h <= t) { int u = q[h++]; for (int i = head[u]; i; i = e[i].nxt) { int v = e[i].to; if (!d[v] && e[i].w) { d[v] = d[u] + 1; q[++t] = v; } } } return d[ed]; } int dfs(int x, int low) { if (!low || x == ed) return low; int res = 0, w, v; for (int i = head[x]; i; i = e[i].nxt) { v = e[i].to; if ((d[v] == d[x] + 1) && e[i].w) { w = dfs(v, min(low - res, e[i].w)); e[i].w -= w; e[i ^ 1].w += w; res += w; } } if (!res) d[x] = -1; return res; } void dinic() { int ans = 0; while (bfs()) ans += dfs(st, inf); printf("%d ", sum - ans); } int main() { scanf("%d%d%d", &n, &m, &k); for (int i = 1; i <= n; i++) scanf("%d", &l[i]); for (int i = 1; i <= m; i++) scanf("%d", &c[i]); for (int i = 1; i < k; i++) { int a, b; scanf("%d%d", &a, &b); v[a][b] = true; } for (int i = 1; i <= n; i++) { for (int j = 1; j <= m; j++) { L[i] += !v[i][j]; C[j] += !v[i][j]; if (i == n) { if (C[j] < c[j]) { puts("JIONG!"); return 0; } } } if (L[i] < l[i]) { puts("JIONG!"); return 0; } sum += L[i]; } st = 0, ed = n + m + 1; for (int i = 1; i <= n; i++) { add(st, i, L[i] - l[i]); } for (int i = 1; i <= m; i++) { add(i + n, ed, C[i] - c[i]); } for (int i = 1; i <= n; i++) { for (int j = 1; j <= m; j++) { if (!v[i][j]) add(i, j + n, 1); } } dinic(); return 0; }