题目链接:https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=2414
Solution
看到求最小大炮轰的次数我们很容易想到二分图的最小点覆盖,就是求最少哪些行或者列开出炮弹可以使得全部怪物被打死掉,那么我们行列建图,求一个最大匹配就行了
数据范围匈牙利即可,跑完一个最大匹配后,得到了数字答案,那么问题来了,如何得到在哪些行或者列打出炮弹呢。
我们结合图上来看
对于这个图,我们跑完匈牙利匹配后,1和2有连接,3和3有连接,我们输出应该是c2,r3/c3
可以这样想,左边已经匹配完成的点不管它,我们把左边没有匹配的点重新匹配,这时候重新被访问的左端节点就是不能开出炮弹的行(有更优的列值)
怎么说呢,我们看图模拟一边,首先左点2没有连接,从它开始匹配,找到右2,右2已经有匹配左点1,试图给左1分配一个新的点,然而没有,增广失败,
这次寻找增广路我们从左2找到了右2再到左1终止,那这时就把左点1,2加入不可开炮队列,而将右点2加入可开炮队列(因为右点2可以一枪打死俩,更加优秀)
而重新访问到的右点则是需要开出炮弹的列,这种情况在上图没有出现,和上面讨论类似。
1 /* 2 最小点覆盖+找出最小点覆盖的点集 3 */ 4 #include <bits/stdc++.h> 5 #define lson rt << 1, l, mid 6 #define rson rt << 1 | 1, mid + 1, r 7 using namespace std; 8 using ll = long long; 9 using ull = unsigned long long; 10 using pa = pair<int, int>; 11 using ld = long double; 12 int n, m, k; 13 const int maxn = 1e3 + 10; 14 template <class T> 15 inline T read(T &ret) 16 { 17 int f = 1; 18 ret = 0; 19 char ch = getchar(); 20 while (!isdigit(ch)) 21 { 22 if (ch == '-') 23 f = -1; 24 ch = getchar(); 25 } 26 while (isdigit(ch)) 27 { 28 ret = (ret << 1) + (ret << 3) + ch - '0'; 29 ch = getchar(); 30 } 31 ret *= f; 32 return ret; 33 } 34 template <class T> 35 inline void write(T n) 36 { 37 if (n < 0) 38 { 39 putchar('-'); 40 n = -n; 41 } 42 if (n >= 10) 43 { 44 write(n / 10); 45 } 46 putchar(n % 10 + '0'); 47 } 48 int llink[maxn], rlink[maxn], visl[maxn], visr[maxn]; 49 vector<int> g[maxn]; 50 inline void init() 51 { 52 memset(llink, -1, sizeof(int) * (m + 2)); 53 memset(rlink, -1, sizeof(int) * (n + 2)); 54 for (int i = 1; i <= n; i++) 55 g[i].clear(); 56 } 57 inline void add(int x, int y) 58 { 59 g[x].emplace_back(y); 60 } 61 int find(int u) 62 { 63 visl[u] = 1; 64 int sz = g[u].size(); 65 for (int i = 0; i < sz; i++) 66 { 67 int v = g[u][i]; 68 if (!visr[v]) 69 { 70 visr[v] = 1; 71 if (llink[v] == -1 || find(llink[v])) 72 { 73 llink[v] = u; 74 rlink[u] = v; 75 return 1; 76 } 77 } 78 } 79 return 0; 80 } 81 int maxMacth() 82 { 83 int res = 0; 84 for (int i = 1; i <= n; i++) 85 { 86 memset(visr, 0, sizeof(int) * (m + 1)); 87 res += find(i); 88 } 89 return res; 90 } 91 int main(int argc, char const *argv[]) 92 { 93 ios::sync_with_stdio(false); 94 cin.tie(0); 95 cout.tie(0); 96 #ifndef ONLINE_JUDGE 97 freopen("in.txt", "r", stdin); 98 freopen("out.txt", "w", stdout); 99 #endif 100 while (cin >> n >> m >> k && (m + n + k)) 101 { 102 init(); 103 for (int i = 0; i < k; i++) 104 { 105 int x, y; 106 cin >> x >> y; 107 add(x, y); 108 } 109 int ans = maxMacth(); 110 memset(visl, 0, sizeof visl); 111 memset(visr, 0, sizeof visr); 112 for (int i = 1; i <= n; i++) //再次匹配,找到哪些点需要匹配 113 if (rlink[i] == -1) 114 find(i); 115 cout << ans << " "; 116 for (int i = 1; i <= n; i++) 117 if (!visl[i]) 118 cout << "r" << i << " "; 119 for (int i = 1; i <= m; i++) 120 if (visr[i]) 121 cout << "c" << i << ' '; 122 cout << "\n"; 123 } 124 return 0; 125 }View Code