参考博客:如何找取 最小覆盖点集合
题意:R*C大小的网格,网格上面放了一些目标。可以再网格外发射子弹,子弹会沿着垂直或者水平方向飞行,并且打掉飞行路径上的所有目标,计算最小多少子弹,各从哪些位置发射,才能将所有的目标全部打掉
分析:就是求最小覆盖点 以及 输出所有的覆盖点
最小覆盖点 == 最大匹配数
个人理解:
最大匹配数是用匈牙利算法求的,就是从左边一个点开始找到他看上的那个妹纸,如果那个妹纸已经名花有有主就跟那个男生换一个...沿着 ( 没匹配 - 匹配边 -... - 没匹配边) 这样的增广路径来探寻。
假设已经求得最大匹配,怎么证明最大匹配等于最小覆盖点呢, 首先从左边没有匹配上的边开始 找增光路,找到所有的点都标记一下,当然不会找到一个全的,如果全的话就不是最大匹配,又多出来一个匹配边。 那么最小覆盖点就是 左边 没标记 + 右边标记的, 为什么呢? 对于左边没标记的 也就是说他有 匹配边 同时与他相连的右边那个点 没被标记,(如果右边标记, 他就被标记了),所有左边 所有没标记的点 是 最大匹配边的一部分 , 然后右边标记的呢,从左边没标记点开始连向右边的点,右边的这个点一定被匹配上了的,右边标记的点也是匹配边的一个端点,组成了 最大匹配边的那一部分, 说白点就是 左边没标记的匹配边 就是这两个点就是相互喜欢型的,而右边标记的就是有别人暗恋的。
1 #include <iostream> 2 #include <cstring> 3 #include <algorithm> 4 #include <cstdio> 5 #include <vector> 6 using namespace std; 7 const int Max = 1000 + 10; 8 int n, m; 9 vector<int> G[Max]; 10 int Left[Max], Right[Max]; // left[i]表示右边的i对应左边的编号,right[i]表示左边的i对应右边的编号 11 bool vis[Max]; // 右边的点有没有访问 12 bool S[Max]; // 左边的点有没有没标记 13 void init() 14 { 15 for (int i = 0; i < n; i++) 16 G[i].clear(); 17 } 18 void addedge(int u, int v) 19 { 20 G[u].push_back(v); 21 } 22 bool mach(int u) 23 { 24 S[u] = true; 25 int len = (int) G[u].size(); 26 for (int i = 0; i < len; i++) 27 { 28 int v = G[u][i]; 29 if (!vis[v]) 30 { 31 vis[v] = true; 32 if (Left[v] == -1 || mach(Left[v])) 33 { 34 Left[v] = u; // 存储匹配关系 35 Right[u] = v; 36 return true; 37 } 38 } 39 } 40 return false; 41 } 42 int solve() 43 { 44 memset(Left, -1, sizeof(Left)); 45 memset(Right, -1, sizeof(Right)); 46 int ans = 0; 47 for (int i = 0; i < n; i++) 48 { 49 memset(vis, 0, sizeof(vis)); 50 if (mach(i)) 51 ans++; 52 } 53 return ans; 54 } 55 int mincover(vector<int> & X, vector<int> & Y) 56 { 57 int ans = solve(); 58 memset(vis, 0, sizeof(vis)); 59 memset(S, 0, sizeof(S)); 60 for (int i = 0; i < n; i++) 61 { 62 if (Right[i] == -1) // 从左边没有匹配的点开始找增广路 63 mach(i); 64 } 65 for (int i = 0; i < n; i++) 66 if (!S[i]) // 左边没标记 67 X.push_back(i); 68 for (int i = 0; i < m; i++) 69 if (vis[i]) // 右边标记的 70 Y.push_back(i); 71 return ans; 72 } 73 int main() 74 { 75 int c, r, N; 76 while (scanf("%d%d%d", &n, &m, & N) != EOF) 77 { 78 if (n == 0 && m == 0 && N == 0) 79 break; 80 init(); 81 for (int i = 0; i < N; i++) 82 { 83 scanf("%d%d", &r, &c); 84 r--; 85 c--; 86 addedge(r, c); 87 } 88 vector<int> X, Y; 89 int ans = mincover(X, Y); 90 printf("%d", ans); 91 for (int i = 0; i < (int) X.size(); i++) 92 printf(" r%d", X[i] + 1); 93 for (int j = 0; j < (int) Y.size(); j++) 94 printf(" c%d", Y[j] + 1); 95 printf(" "); 96 } 97 98 return 0; 99 }