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
",solve());
73     }
74     return 0;
75 }
View Code
原文地址:https://www.cnblogs.com/zhanzhao/p/3900333.html