一,原题链接:http://poj.org/problem?id=1422
二.题目大意:抽象成裸数学,就是给你一个有向图(无环),让你求最小路径覆盖,就是在一个图的节点上放伞兵,伞兵只能往回走,走遍所有的点需要放多少伞兵。
三,思路:如果直接知道 最小路径覆盖 = 顶点数 - 最大匹配,(这里的匹配是把图中每个点拆为2个点,如果在原图中有边,则左边到右边连一条线。
那直接套模板(逃)完了。不过这样就不好玩了是吧。
1.假设原图中一条街道都没有,伞兵要遍历所有节点,那么就需要n个伞兵。(n为节点数)
2.从U->V增加一条边,那么V上的伞兵就可以不放了。
3.这时从U->V‘再增加一条边,发现V'还是要放伞兵,因为U的伞兵已经走了。
4.再从U'->V增加一条边,发现V已经被遍历过了,因此总伞兵数不会减少。
问题转换成在原图中找使得所有顶点的入度和出度不超过1的边,每找一条边,就能减去一个兵。
如果按下面方式建图:
1.图中每个顶点拆为2个,分居二分图2边。
2,如果在原图中有边,则从左边连到右边。
这样求出来的最大匹配对应的就是原图中使得所有顶点入度和出度不超过1的边,左边对应出度,右边对应入度。
而以上推理过程,只要套上相应的数学名词,就是最小路径覆盖 = 顶点数 - 最大匹配的证明了。
四,代码
#include <iostream> #include <cstdio> #include <queue> #include <cstring> #include <cstdlib> using namespace std; const int INF = 0x3f3f3f3f, MAX_N = 121; class maxMatch { public: bool connected[MAX_N][MAX_N]; bool visited[MAX_N]; int xMatch[MAX_N], yMatch[MAX_N], xNum, yNum; maxMatch() { xNum = yNum = 0; memset(connected, 0, sizeof(connected)); memset(xMatch, -1, sizeof(xMatch)); memset(yMatch, -1, sizeof(yMatch)); } bool path(int u) { int v; for(v = 1; v <= yNum; v++) if(connected[u][v] && !visited[v]){ visited[v] = true; if(-1 == yMatch[v] || path(yMatch[v])){ yMatch[v] = u; xMatch[u] = v; return true; } } return false; } int getRes() { int u, res = 0; for(u = 1; u <= xNum; u++){ memset(visited, 0, sizeof(visited)); if(path(u)) res++; } return res; } }; void solve() { maxMatch G; int nodeNum, edgeNum, u, v; scanf("%d%d", &nodeNum, &edgeNum); G.xNum = G.yNum = nodeNum; while(edgeNum--){ scanf("%d%d", &u, &v); G.connected[u][v] = true; } printf("%d\n", nodeNum-G.getRes()); } int main() { //freopen("in.txt", "r", stdin); int i, j, test; scanf("%d", &test); while(test--) solve(); }