题意:在一个N×N的方格图中,有K个敌人。你有一个武器,发射一次可以消灭一列或者一行的敌人。求最小的发射次数,消灭所有敌人。
思路:因为每次都是消灭一行或一列的敌人,那其实本题的变量是行和列。即选出最少的行和列,消灭完所有的敌人。
因为每个敌人的坐标既包含行也包含列,其实相当于一条边关联了对应的行和列的点。
这样经过转化,我们其实将原图转化成了二分图:行的顶点为一个集合,列的顶点为一个集合。而每个敌人对应点和列相连的边。我消灭所有敌人,即选出一个点的集合,让每个边至少有一个端点在集合中,这也是最小点覆盖的定义。同时,在二分图中,最小点覆盖的数目等于最大匹配的数目。
代码如下:
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <queue>
using namespace std;
const int INF = 0x3f3f3f3f;
struct Edge{
int from, to,cap,flow;
Edge(int u, int v,int c, int f):from(u),to(v),cap(c),flow(f){}
};
struct ISAP{
static const int MAX = 1050;
vector<Edge> edges;
vector<int> G[MAX];
bool vis[MAX];
int p[MAX];
int d[MAX];
int num[MAX];
int cur[MAX];
int m,n,s,t;
void init(int n){
this->n = n;
for(int i = 0; i < n; ++i)
G[i].clear();
edges.clear();
}
void addedge(int from, int to, int cap){
edges.push_back(Edge(from,to,cap,0));
edges.push_back(Edge(to,from,0,0));
m = edges.size();
G[from].push_back(m-2);
G[to].push_back(m-1);
}
void bfs(){
memset(vis,0,sizeof(vis));
queue<int> Q;
d[t] = 0;
vis[t] = true;
Q.push(t);
while(!Q.empty()){
int x = Q.front(); Q.pop();
for(int i = 0 , sz = G[x].size(); i < sz; ++i){
Edge & e = edges[G[x][i]^1];
if(!vis[e.from] && e.cap > e.flow){
vis[e.from] = true;
d[e.from] = d[x] + 1;
Q.push(e.from);
}
}
}
}
int Augment(){
int x = t, a = INF;
while(x != s){
Edge& e = edges[p[x]];
a = min(a,e.cap - e.flow);
x = edges[p[x]].from;
}
x = t;
while(x != s){
edges[p[x]].flow += a;
edges[p[x]^1].flow -= a;
x = edges[p[x]].from;
}
return a;
}
int maxflow(int s, int t){
this->s = s, this->t = t;
int flow = 0 ;
bfs();
memset(num,0,sizeof(num));
memset(cur,0,sizeof(cur));
for(int i = 0; i < n; ++i)
num[d[i]]++;
int x = s;
while(d[s] < n){
if(x == t){
flow += Augment();
x = s;
}
bool ok = false;
for(int i = cur[x], sz = G[x].size(); i < sz; ++i){
Edge& e = edges[G[x][i]];
if(d[x] == d[e.to] + 1 && e.flow < e.cap){
ok = true;
p[e.to] = G[x][i];
cur[x] = i;
x = e.to;
break;
}
}
if(!ok){
int m = n - 1;
for(int i = 0, sz = G[x].size(); i < sz; ++i){
Edge& e = edges[G[x][i]];
if(e.cap > e.flow) m = min(m,d[e.to]);
}
if(--num[d[x]] == 0) break;
num[d[x] = m + 1]++;
cur[x] = 0;
if(x != s) x = edges[p[x]].from;
}
}
return flow;
}
}solver;
int N,K;
int r,c;
int main(void)
{
//freopen("input.txt","r",stdin);
scanf("%d %d", &N,&K);
int s = 0, t = 2 * N + 1;
solver.init(2 * N + 2);
for(int i = 1; i <= N; ++i){
solver.addedge(s,i,1);
solver.addedge(i + N,t,1);
}
for(int i = 0; i < K; ++i){
scanf("%d %d", &r,&c);
solver.addedge(r,c + N,1);
}
printf("%d\n",solver.maxflow(s,t));
return 0;
}