UVA 11419 SAM I AM

SAM I AM

Time Limit: 7000ms
Memory Limit: 131072KB
This problem will be judged on UVA. Original ID: 11419
64-bit integer IO format: %lld      Java class name: Main
 

The world is in great danger!! Mental's forces have returned to Earth to eradicate humankind. Our last hope to stop this great evil is Sam Serious Stone. Equipped with various powerful weapons, Serious Sam starts his mission to destroy the forces of evil.

After fighting two days and three nights, Sam is now in front of the temple KOPTOS where Mental's general Ugh Zan III is waiting for him. But this time, he has a serious problem. He is in shortage of ammo and a lot of enemies crawling inside the temple waiting for him. After rounding the temple Sam finds that the temple is in rectangle shape and he has the locations of all enemies in the temple.

All of a sudden he realizes that he can kill the enemies without entering the temple using the great cannon ball which spits out a gigantic ball bigger than him killing anything it runs into and keeps on rolling until it finally explodes. But the cannonball can only shoot horizontally or vertically and all the enemies along the path of that cannon ball will be killed.

Now he wants to save as many cannon balls as possible for fighting with Mental. So, he wants to know the minimum number of cannon balls and the positions from which he can shoot the cannonballs to eliminate all enemies from outside that temple.

 

Input

Here, the temple is defined as a RXC grid. The first line of each test case contains 3 integers: R(0<R<1001), C(0<C<1001) representing the grid of temple (R means number of row and C means number of column of the grid) and the number of enemies N(0<N<1000001) inside the temple. After that there are N lines each of which contains 2 integers representing the position of the enemies in that temple. Each test case is followed by a new line (except the last one). Input is terminated when R=C=N=0. The size of the input file is around 1.3 MB.

Output

For each test case there will be one line output. First print the minimum number (m) of cannonballs needed to wipe out the enemies followed by a single space and then m positions from which he can shoot those cannonballs. For shooting horizontally print r followed by the row number and for vertical shooting print c followed by the column number. If there is more than one solution any one will do.

Sample Input

4 4 3

1 1

1 4

3 2

4 4 2

1 1

2 2

0 0 0

Output for Sample Input

2 r1 r3

2 r1 r2

解题:最小点覆盖,最小点覆盖等于最大匹配数,关键在于如何把点覆盖中的点输出出来

来自Matrix67大神的解说

二分图最大匹配的König定理及其证明 
本文将是这一系列里最短的一篇,因为我只打算把König定理证了,其它的废话一概没有。 
以下五个问题我可能会在以后的文章里说,如果你现在很想知道的话,网上去找找答案: 
1. 什么是二分图; 
2. 什么是二分图的匹配; 
3. 什么是匈牙利算法;(http://www.matrix67.com/blog/article.asp?id=41) 
4. König定理证到了有什么用; 
5. 为什么o上面有两个点。 

König定理是一个二分图中很重要的定理,它的意思是,一个二分图中的最大匹配数等于这个图中的最小点覆盖数。如果你还不知道什么是最小点覆盖,我也在这里说一下:假如选了一个点就相当于覆盖了以它为端点的所有边,你需要选择最少的点来覆盖所有的边。比如,下面这个图中的最大匹配和最小点覆盖已分别用蓝色和红色标注。它们都等于3。这个定理相信大多数人都知道,但是网络上给出的证明并不多见。有一些网上常见的“证明”明显是错误的。因此,我在这里写一下这个定理的证明,希望对大家有所帮助。 


假如我们已经通过匈牙利算法求出了最大匹配(假设它等于M),下面给出的方法可以告诉我们,选哪M个点可以覆盖所有的边。 
匈牙利算法需要我们从右边的某个没有匹配的点,走出一条使得“一条没被匹配、一条已经匹配过,再下一条又没匹配这样交替地出现”的路(交错轨,增广路)。但是,现在我们已经找到了最大匹配,已经不存在这样的路了。换句话说,我们能寻找到很多可能的增广路,但最后都以找不到“终点是还没有匹配过的点”而失败。我们给所有这样的点打上记号:从右边的所有没有匹配过的点出发,按照增广路的“交替出现”的要求可以走到的所有点(最后走出的路径是很多条不完整的增广路)。那么这些点组成了最小覆盖点集:右边所有没有打上记号的点,加上左边已经有记号的点。看图,右图中展示了两条这样的路径,标记了一共6个点(用 “√”表示)。那么,用红色圈起来的三个点就是我们的最小覆盖点集。 
首先,为什么这样得到的点集点的个数恰好有M个呢?答案很简单,因为每个点都是某个匹配边的其中一个端点。如果右边的哪个点是没有匹配过的,那么它早就当成起点被标记了;如果左边的哪个点是没有匹配过的,那就走不到它那里去(否则就找到了一条完整的增广路)。而一个匹配边又不可能左端点是标记了的,同时右端点是没标记的(不然的话右边的点就可以经过这条边到达了)。因此,最后我们圈起来的点与匹配边一一对应。 
其次,为什么这样得到的点集可以覆盖所有的边呢?答案同样简单。不可能存在某一条边,它的左端点是没有标记的,而右端点是有标记的。原因如下:如果这条边不属于我们的匹配边,那么左端点就可以通过这条边到达(从而得到标记);如果这条边属于我们的匹配边,那么右端点不可能是一条路径的起点,于是它的标记只能是从这条边的左端点过来的(想想匹配的定义),左端点就应该有标记。 
最后,为什么这是最小的点覆盖集呢?这当然是最小的,不可能有比M还小的点覆盖集了,因为要覆盖这M条匹配边至少就需要M个点(再次回到匹配的定义)。 

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 const int maxn = 1005;
 4 vector<int>g[maxn];
 5 bool T[maxn],S[maxn];
 6 int Link[maxn],n,m,k,Link2[maxn];
 7 bool match(int u) {
 8     S[u] = true;
 9     for(int i = g[u].size()-1; i >= 0; --i) {
10         if(!T[g[u][i]]) {
11             T[g[u][i]] = true;
12             if(Link[g[u][i]] == -1 || match(Link[g[u][i]])) {
13                 Link[g[u][i]] = u;
14                 Link2[u] = g[u][i];
15                 return true;
16             }
17         }
18     }
19     return false;
20 }
21 int main() {
22     int u,v;
23     vector<int>X,Y;
24     while(scanf("%d%d%d",&n,&m,&k),n||m||k) {
25         for(int i = 0; i < maxn; ++i) g[i].clear();
26         for(int i = 0; i < k; ++i) {
27             scanf("%d%d",&u,&v);
28             g[u].push_back(v);
29         }
30         int ret = 0;
31         memset(Link,-1,sizeof Link);
32         memset(Link2,-1,sizeof Link2);
33         for(int i = 1; i <= n; ++i) {
34             memset(S,false,sizeof S);
35             memset(T,false,sizeof T);
36             if(match(i)) ++ret;
37         }
38         X.clear();
39         Y.clear();
40         memset(S,false,sizeof S);
41         memset(T,false,sizeof T);
42         for(int i = 1; i <= n; ++i)
43             if(Link2[i] == -1) match(i);
44         for(int i = 1; i <= n; ++i)
45             if(!S[i]) X.push_back(i);
46         for(int j = 1; j <= m; ++j)
47             if(T[j]) Y.push_back(j);
48         printf("%d",ret);
49         for(int i = 0; i < X.size(); ++i)
50             printf(" r%d",X[i]);
51         for(int j = 0; j < Y.size(); ++j)
52             printf(" c%d",Y[j]);
53         putchar('
');
54     }
55     return 0;
56 }
View Code
原文地址:https://www.cnblogs.com/crackpotisback/p/4695276.html