士兵佔領(網路最大流+建圖)
阿新 • • 發佈:2020-11-12
題目描述
有一個M * N的棋盤,有的格子是障礙。現在你要選擇一些格子來放置一些士兵,一個格子裡最多可以放置一個士兵,障礙格里不能放置士兵。我們稱這些士兵佔領了整個棋盤當滿足第i行至少放置了Li個士兵, 第j列至少放置了Cj個士兵。現在你的任務是要求使用最少個數的士兵來佔領整個棋盤。
輸入格式
第一行兩個數M, N, K分別表示棋盤的行數,列數以及障礙的個數。 第二行有M個數表示Li。 第三行有N個數表示Ci。 接下來有K行,每行兩個數X, Y表示(X, Y)這個格子是障礙。
輸出格式
輸出一個數表示最少需要使用的士兵個數。如果無論放置多少個士兵都沒有辦法佔領整個棋盤,輸出”JIONG!” (不含引號)
輸入輸出樣例
輸入
4 4 4
1 1 1 1
0 1 0 3
1 4
2 2
3 3
4 3
輸出
4
說明/提示
M, N <= 100, 0 <= K <= M * N Local
Solution
建圖就完lia~
利用二分圖的思想
將行列拆分
行編號\(1 ~ n\)列編號\(n + 1 ~ n + m\)
將問題轉化為
“給定一個站滿士兵的矩陣,矩陣內有一些障礙點,給出每行每列士兵個數限制條件,問最多可以殺掉多少士兵”
那麼對於沒有障礙的點
行列直接建邊,邊權為1
表示當前行列可以殺掉一個士兵
建立超級源點\(s\)和超級匯點\(t\)
從源點向每行建邊
每行可以殺掉的士兵個數為 列數m-當前行的障礙數-當前行必須要有的士兵個數
每列同理,殺掉的士兵數為 行數n-當前列的障礙數-當前列必須要有的士兵個數
答案為矩陣大小-障礙數-最大流(能夠殺掉最多的士兵個數)
Code
#include <iostream> #include <cstring> #include <cstdio> #include <algorithm> #include <queue> #define min(a, b) ({register int AA = a, BB = b; AA < BB ? AA : BB;}) using namespace std; inline int read(){ int x = 0, w = 1; char ch = getchar(); for(; ch > '9' || ch < '0'; ch = getchar()) if(ch == '-') w = -1; for(; ch >= '0' && ch <= '9'; ch = getchar()) x = x * 10 + ch - '0'; return x * w; } const int ss = 10010; struct node{ int to, nxt, w; }edge[ss * 20]; int head[ss], tot = 1; inline void add(register int u, register int v, register int w){ edge[++tot].to = v; edge[tot].nxt = head[u]; edge[tot].w = w; head[u] = tot; } int dis[ss], cur[ss]; int n, m, s, t, k; bool vis[ss]; queue<int> q; inline bool spfa(register int s){ for(register int i = 0; i <= t; i++) dis[i] = 0x3f3f3f3f, cur[i] = head[i]; dis[s] = 0; q.push(s); while(!q.empty()){ register int u = q.front(); q.pop(); vis[u] = 0; for(register int i = head[u]; i; i = edge[i].nxt){ register int v = edge[i].to; if(dis[v] > dis[u] + 1 && edge[i].w){ dis[v] = dis[u] + 1; if(!vis[v]) q.push(v), vis[v] = 1; } } } return dis[t] != 0x3f3f3f3f; } inline int dfs(register int u, register int flow){ register int res = 0; if(u == t) return flow; for(register int i = cur[u]; i; i = edge[i].nxt){ cur[u] = i; register int v = edge[i].to; if(dis[v] == dis[u] + 1 && edge[i].w){ if(res = dfs(v, min(flow, edge[i].w))){ edge[i].w -= res; edge[i ^ 1].w += res; return res; } } } return 0; } long long maxflow = 0; inline long long dinic(){ register long long minflow = 0; while(spfa(s)){ while(minflow = dfs(s, 0x7fffffff)) maxflow += minflow; } return maxflow; } bool map[105][105]; int l[105], c[105]; int cntl[105], cntc[105]; signed main(){ n = read(), m = read(), k = read(); s = 0, t = m + n + 1; for(register int i = 1; i <= n; i++) l[i] = read(); for(register int i = 1; i <= m; i++) c[i] = read(); for(register int i = 1; i <= k; i++){ register int x = read(), y = read(); map[x][y] = 1; } for(register int i = 1; i <= n; i++) for(register int j = 1; j <= m; j++) if(map[i][j]) cntl[i]++, cntc[j]++; for(register int i = 1; i <= n; i++) for(register int j = 1; j <= m; j++) if(!map[i][j]) add(i, j + n, 1), add(j + n, i, 0); for(register int i = 1; i <= n; i++){ add(s, i, m - cntl[i] - l[i]); add(i, s, 0); } for(register int j = 1; j <= m;j++){ add(j + n, t, n - cntc[j] - c[j]); add(t, j + n, 0); } printf("%lld\n", n * m - k - dinic()); return 0; }