此题为个人PK赛的B题,POJ传送门:Asteroids
解题报告:光束的攻击选择可以是横坐标从x=1到x=n和纵坐标从y=1到y=n,一共2n种。显然,同样的选择没有必要执行多次,而攻击的顺序对结果没有影响,所以总的攻击方案共有22n种。我们只要在这个解空间中,寻找能够摧毁所有小行星的最小的解就可以了。如图:
把光束当作图的顶点,而把小行星当作连接对应光束的边。这样去转换,光束的攻击方案即对应一个顶点集合S,而要求攻击方案能够摧毁所有的小行星,也就是图中每条边都至少有一个属于S的端点。这样一来,问题就转为了求最小的满足上述要求的顶点集合S。
所以就是最小顶点覆盖问题。在二分图中等于最大匹配。本题中所有顶点可以分成水平和竖直两个方向的攻击选择,而每颗小行星所对应的便都分别与一个水平方向和一个竖直方向的顶点相连,所以是二分图。所以只要运用二分图最大匹配算法即可。代码如下:
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
int maps[1010][1010];
bool used[1010];
int match[1010];
int n,k;
bool dfs(int u){
for (int v = 1; v <= n; ++v){
if (maps[u][v]&&!used[v]){
used[v]=true;
if (match[v]==-1||dfs(match[v])){
match[v]=u;
return true;
}
}
}
return false;
}
int maxmatch(){
int res=0;
memset(match,-1,sizeof(match));
for (int u = 1; u <= n; ++u){
memset(used,false,sizeof(used));
if (dfs(u))
res++;
}
return res;
}
int main(){
while(scanf("%d%d",&n,&k)==2){
int x,y;
memset(maps,0,sizeof(maps));
for (int i = 1; i <= k; ++i){
scanf("%d%d",&x,&y);
maps[x][y]=1;
}
printf("%d\n", maxmatch());
}
}