HDU 1151 Air Raid
http://acm.hdu.edu.cn/showproblem.php?pid=1151
题意:一个城市有m个十字路口(点),n条街道(边),街道是单向的,而且该城市的所有街道不存在环路,现在你要派伞兵空袭,伞兵随机降落在任意的十字路口上,从该十字路口出发能到达的十字路口,该伞兵都能到达(只能选一个到达,也就是说一个伞兵最多2个路口),但只能延一方向前进,不能回头,问,最少需要多少伞兵,能访问该城市的所有十字路口。
题目还说"It is also known that starting from an intersection and walking through town's streets you can never reach the same intersection"。
可以构造一个二分图,X部是N个节点,Y部同样也是N个节点,匹配一条边说明伞兵从降落点到达访问一个相邻点;不被匹配的点就表示伞兵只访问降落点,与其相邻的点已被其它伞兵访问过了,要使伞兵最少,当然就要尽可能比配更多边,因为一个伞兵就访问了2个点,总比只访问1个点来的划算。X部中被匹配的点只能被匹配一次,一个匹配可以当成从一个X部出发到另一个点,属于一个士兵路径中的一小部分,Y部未被匹配的点就是一条路径的起点。
答案是求最小路径覆盖。最小路径覆盖=|P|(节点数)-最大匹配数
对于公式:最小路径覆盖=|P|-最大匹配数;可以这么来理解;
如果匹配数为零,那么P中不存在有向边,于是显然有:
最小路径覆盖=|P|-最大匹配数=|P|-0=|P|;即P的最小路径覆盖数为|P|;
P'中不在于匹配边时,路径覆盖数为|P|;
如果在P'中增加一条匹配边pi'-->pj'',那么在图P的路径覆盖中就存在一条由pi连接pj的边,也就是说pi与pj 在一条路径上,于是路径覆盖数就可以减少一个;
如此继续增加匹配边,每增加一条,路径覆盖数就减少一条;直到匹配边不能继续增加时,路径覆盖数也不能再减少了,此 时就有了前面的公式;但是这里只 是说明了每条匹配边对应于路径覆盖中的一条路径上的一条连接两个点之间的有向边;下面来说明一个路径覆盖中的每条连接两个顶点之间的有向边对应于一条匹配 边;
与前面类似,对于路径覆盖中的每条连接两个顶点之间的每条有向边pi--->pj,我们可以在匹配图中对应做 一条连接pi'与pj''的边, 显然这样做出来图的是一个匹配图(这一点用反证法很容易证明,如果得到的图不是一个匹配图,那么这个图中必定存在这样两条边 pi'---pj'' 及 pi' ----pk'',(j!=k),那么在路径覆盖图中就存在了两条边pi-->pj, pi--->pk ,那边从pi出发的路径就不止一条了,这与路径覆盖图是矛盾的;还有另外一种情况就是存在pi'---pj'',pk'---pj'',这种情况也类似可 证);
1 #include <cstring> 2 #include <cstdlib> 3 #include <cstdio> 4 #define MAXN 125 5 using namespace std; 6 7 int N, M, G[MAXN][MAXN], marry[MAXN], visit[MAXN]; 8 9 int path(int u) //找增广路 10 { 11 for (int i = 1; i <= N; ++i) { 12 if (!G[u][i] || visit[i]) { 13 continue; 14 } 15 visit[i] = 1; 16 if (!marry[i] || path(marry[i])) { 17 marry[i] = u; 18 return 1; 19 } 20 } 21 return 0; 22 } 23 24 int main() 25 { 26 int T, x, y, ans; 27 scanf("%d", &T); 28 while (T--) { 29 ans = 0; 30 memset(G, 0, sizeof (G)); 31 memset(marry, 0, sizeof (marry)); 32 scanf("%d %d", &N, &M); 33 for (int i = 0; i < M; ++i) { 34 scanf("%d %d", &x, &y); 35 G[x][y] = 1; 36 } 37 for (int i = 1; i <= N; ++i) { 38 memset(visit, 0, sizeof (visit)); 39 if (path(i)) ++ans; 40 } 41 printf("%d\n", N-ans); 42 } 43 return 0; 44 }