POJ3041Asteroids【最小点覆盖】
大意:
X.X
.X.
.X. 如左图X代表怪物你可以用激光去消灭它们,一次可以消灭同一行或同一列的所有怪物,问最少多少次才可以把所有怪物消灭完
思路:把x,y坐标分别看成左集合与右集合,若两个坐标有联系就建一条边
最后求得是最小点覆盖
正确性证明:每个怪物的坐标都是由一对x,y确定的,在二分图中每条边就代表一对坐标点,所以只要把所有的边被覆盖就可以了
代码:
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <algorithm> 5 #include <set> 6 using namespace std; 7 8 const int maxn = 505; 9 10 struct Node { 11 int to, next; 12 }p[10005]; 13 14 int head[10005]; 15 16 int tot; 17 18 int X[maxn]; 19 int x_cnt; 20 int vis[maxn]; 21 int link[maxn]; 22 23 void AddEdge(int u, int v) { 24 p[tot].to = v; 25 p[tot].next = head[u]; 26 head[u] = tot ++; 27 } 28 29 bool Find(int u) { 30 for(int i = head[u]; i; i = p[i].next) { 31 int v = p[i].to; 32 if(!vis[v]) { 33 vis[v] = 1; 34 if(link[v] == -1 || Find(link[v])) { 35 link[v] = u; 36 return true; 37 } 38 } 39 } 40 return false; 41 } 42 int n; 43 44 int solve() { 45 int cnt = 0; 46 memset(link, -1, sizeof(link)); 47 memset(vis, 0, sizeof(vis)); 48 if(Find(X[0])) cnt++; 49 for(int i = 1; i < x_cnt; i++) { 50 if(X[i] == X[i - 1]) continue; 51 memset(vis, 0, sizeof(vis)); 52 if(Find(X[i])) cnt++; 53 } 54 return cnt; 55 } 56 57 int main() { 58 int k; 59 int u, v; 60 //freopen("3041.txt","r",stdin); 61 while(EOF != scanf("%d %d",&n, &k)) { 62 tot = 1; 63 x_cnt = 0; 64 memset(head, 0, sizeof(head)); 65 //memset(X, 0, sizeof(X)); 66 for(int i = 0; i < k; i++) { 67 scanf("%d %d",&u, &v); 68 AddEdge(u, v); 69 X[x_cnt++] = u; 70 } 71 sort(X, X + x_cnt); 72 printf("%d\n",solve()); 73 } 74 return 0; 75 }