题面
题面:给出一个\(R\times C\)大小的网格,网格上面放了一些目标。可以在网格外发射子弹,子弹会沿着垂直或者水平方向飞行,并且打掉飞行路径上的所有目标,如图所示。你的任务是计算出最少需要多少子弹,各从哪些位置发射,才能把所有目标全部打掉。
感觉这就是典型的算法模板题,第一次可能想不出来,第二次就会了。
建立一个二分图:左部点代表每一行,右部点代表每一列。如果一个目标在第\(i\)行第\(j\)列,就连一条\(i \to j\)的边。
那么我们的目标是选择最少的点,使每一条边至少有一个端点被选中,即二分图的最小点覆盖。根据König定理,最小点覆盖数=最大匹配数,所以直接跑匈牙利算法即可。
但关于输出方案我还没有看懂,待以后补上。
这里给两篇参考题解:
https://www.cnblogs.com/colazcy/p/11515004.html
https://blog.csdn.net/z309241990/article/details/34417693
#include<cstdio>
#include<iostream>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<cstdlib>
#include<cctype>
#include<vector>
#include<queue>
#include<assert.h>
#include<ctime>
using namespace std;
#define enter puts("")
#define space putchar(' ')
#define Mem(a, x) memset(a, x, sizeof(a))
#define In inline
#define forE(i, x, y) for(int i = head[x], y; ~i && (y = e[i].to); i = e[i].nxt)
typedef long long ll;
typedef double db;
const int INF = 0x3f3f3f3f;
const db eps = 1e-8;
const int maxn = 1e3 + 5;
const int maxe = 1e6 + 5;
In ll read()
{
ll ans = 0;
char ch = getchar(), las = ' ';
while(!isdigit(ch)) las = ch, ch = getchar();
while(isdigit(ch)) ans = (ans << 1) + (ans << 3) + ch - '0', ch = getchar();
if(las == '-') ans = -ans;
return ans;
}
In void write(ll x)
{
if(x < 0) x = -x, putchar('-');
if(x >= 10) write(x / 10);
putchar(x % 10 + '0');
}
In void MYFILE()
{
#ifndef mrclr
freopen("ha.in", "r", stdin);
freopen("ha.out", "w", stdout);
#endif
}
int n, m, K;
struct Edge
{
int nxt, to;
}e[maxe];
int head[maxn], ecnt = -1;
In void addEdge(int x, int y)
{
e[++ecnt] = (Edge){head[x], y};
head[x] = ecnt;
}
bool vx[maxn], vy[maxn];
int lft[maxn], rgt[maxn];
In bool dfs(int now)
{
vx[now] = 1;
forE(i, now, v) if(!vy[v])
{
vy[v] = 1;
if(!lft[v] || dfs(lft[v])) {lft[v] = now; rgt[now] = v; return 1;}
}
return 0;
}
In int hung()
{
int ret = 0; Mem(lft, 0), Mem(rgt, 0);
for(int i = 1; i <= n; ++i)
{
Mem(vx, 0), Mem(vy, 0);
if(dfs(i)) ++ret;
}
return ret;
}
In void Find(int ans)
{
Mem(vx, 0), Mem(vy, 0);
for(int i = 1; i <= n; ++i) if(!rgt[i]) dfs(i);
write(ans);
for(int i = 1; i <= n; ++i) if(!vx[i]) space, putchar('r'), write(i);
for(int i = 1; i <= m; ++i) if(vy[i]) space, putchar('c'), write(i);
enter;
}
int main()
{
// MYFILE();
while(scanf("%d%d%d", &n, &m, &K) && (n | m | K))
{
Mem(head, -1), ecnt = -1;
for(int i = 1; i <= K; ++i)
{
int x = read(), y = read();
addEdge(x, y);
}
Find(hung());
}
return 0;
}