重点说一下二分图的两个部分,可以明确的是,車所在的一行一列不能再放置了,可以将这个位置,看作是車所在的行和列之间的连线,由此我们将行与列分开,可以看出,行的集合内部,没有连线,即没有哪一个車同时对多行起作用,同理,列也是如此,也就是说我们得到了一张二分图。如果某一个点不能放置,可以看作这一行和这一列之间没有边连接。然后根据这个建图,求最大匹配就是答案。
#include <bits/stdc++.h>
using namespace std;
#define mem(a, b) memset(a, b, sizeof a)
const int N = 210, inf = 0x3f3f3f3f;
bool can[N][N];
int n, m, T;
int s, t;
int head[101000], nex[101000], to[101000], ed[101000], cnt;
void pre(){
mem(can, true);
s = 1000;
t = 1001;
cnt = 1;
mem(head, -1);
mem(nex, -1);
}
void add(int a, int b, int c){
to[++cnt] = b, nex[cnt] = head[a], head[a] = cnt, ed[cnt] = c;
to[++cnt] = a, nex[cnt] = head[b], head[b] = cnt, ed[cnt] = 0;
}
int d[101000], cur[101000];
bool bfs(){
queue<int > q;
cur[s] = head[s];
cur[t] = head[t];
for (int i = 1; i <= n; i++)cur[i] = head[i];
for (int i = 1; i <= m; i++)cur[i + 210] = head[i + 210];
q.push(s);
mem(d, 0);
d[s] = 1;
while (q.size()){
int tp = q.front();
q.pop();
for (int i = head[tp]; ~i; i = nex[i]){
if (!d[to[i]] && ed[i]){
d[to[i]] = d[tp] + 1;
q.push(to[i]);
if (to[i] == t)return 1;
}
}
}
return 0;
}
int dinic(int x, int f){
if (x == t || f == 0)return f;
int flow = 0;
int k;
for (int i = cur[x]; ~i; i = nex[i]){
cur[x] = i;
int y = to[i];
if (d[y] == d[x] + 1 && ed[i]){
k = dinic(y, min(f, ed[i]));
if (!k){
d[y] = 0;
}
ed[i] -= k;
ed[i ^ 1] += k;
flow += k;
if (f - flow == 0)break;
}
}
return flow;
}
int maxflow(){
int f = 0;
while (bfs()){
f += dinic(s, inf);
}
return f;
}
int main(){
ios::sync_with_stdio(false);
pre();
cin >> n >> m >> T;
while (T--){
int x, y;
cin >> x >> y;
can[x][y] = 0;
}
// 行 和 列 是二分图的两部分
for (int i = 1; i <= n; i++){
add(s, i, 1);
for (int j = 1; j <= m; j++){
if (can[i][j]){
add(i, j + 210, 1);
}
}
}
for (int i = 1; i <= m; i++)add(i + 210, t, 1);
cout << maxflow() << "\n";
return 0;
}