poj 3041 Asteroids 二分图-最小覆盖点
题意,
N*N 矩阵, 有些地方有行星, 现有一种武器能够攻击一行, 或者一列,问最少攻击次数.消灭所有行星
解法.
因为要消灭所有顶点, 同一行中的顶点能 一次 消灭, 同一列的顶点也能 一次 消灭.
我们需要 最小的 攻击次数, 则意味着 最小的攻击次数消灭 所有顶点.
将所有存在行星的顶点(x,y), 按x, y轴分别作为 A,B顶点集合,转换成二分图.
这样 A,B集合连边的代表一次 攻击 箭支 能消灭的行星, 则题目所求就转换成了.
最小的顶点 覆盖所有边, 既 最小顶点覆盖 问题.
又因为, ( 在所有图中都满足)
最小顶点覆盖 + 最大独立子集 = 顶点数量.
又 在二分图中 有一下定理 (仅仅在二分图中满足)
最大匹配数 + 最大独立子集 = 顶点数量
所以对于本体而言
最小顶点覆盖 = 最大匹配数.
因为顶点数量最多位 N = 500 个, 所以直接构图,然后用 匈牙利算法就可以了. 当然也可以 用网络流. 如果不嫌麻烦...
#include<stdio.h> #include<string.h> #include<stdlib.h> using namespace std; const int N = 510; int ma[N],mb[N]; int n, k; bool g[N][N], vis[N]; bool path( int u ){ for(int v = 1; v <= n; v++){ if( g[u][v] && !vis[v] ){ vis[v] = 1; if( ma[v] == -1 || path( ma[v]) ){ ma[v] = u; mb[u] = v; return true; } } } return false; } int main(){ while( scanf("%d%d", &n,&k) != EOF){ memset( g, 0, sizeof(g)); int a, b; for(int i = 0; i < k; i++){ scanf("%d%d",&a,&b); g[a][b] = 1; } memset( ma, 0xff, sizeof(ma)); memset( mb, 0xff, sizeof(mb)); int res = 0; for(int i = 1; i <= n; i++) { if( mb[i] == - 1){ memset( vis, 0, sizeof(vis)); res += path(i); } } printf("%d\n", res ); } return 0; }