今天做了两个最小路径覆盖的题目,总算是理解了Accept的《最小路径覆盖问题值得注意的地方》 在讲些什么了。
注意:下面博文里所说的最小路径覆盖 与图论里的概念最小路径覆盖 有所区别。
为了便于区分,如果所指的是图论里的最小路径覆盖,会在后面注明;否则默认不是。
最小路径覆盖(图论):
- 就是在图中找最少的路径,使之覆盖了图中的所有顶点,且任何一个顶点有且只有一条路径与之关联(两路径不允许存在交点)
最小路径覆盖:
- 是指在一个有向图中,找出最少的几条路径,用它们来覆盖全图(两个路径允许有交点存在)
以下内容转自:博客Accept
首先,最小路径覆盖(图论)=总节点数-最大匹配数。这个应该已经是路人皆知了。
所谓最小路径覆盖,是指在一个有向图中,找出最少的几条路径,用它们来覆盖全图。
这里说的值得注意的地方,如果有向图的边有相交的情况,那么就不能简单的对原图求二分匹配了
举个例子,假设有图:1->2 2->5 2->3 4->2,事实上,这其实就是两条边:1->5 4->3 ,节点2只是他们的一个交点
如果只是简单的在原图的基础上求二分匹配,那么得到的匹配答案是2,最小路径覆盖答案便是5-2=3。
可是随便一看都能看看出端倪,这个图中,只需要两个点便可以探索完整个地图,这里最小路径覆盖数明显是2。
问题究竟出在哪里呢?其实就和这个交点2有关。既然边有相交,那么他们的连通性也应该连通下去。
解决的办法是对原图进行一次闭包传递(也就是flody),于是便增加了四条边:1->3 1->5 4->3 4->5
这时再求最大匹配数,匹配答案便是3,最小路径覆盖值为2,这是正确答案!
例题:
现在,已经弄明白了两种不同的最小路径覆盖的概念已经对应的求法,下面就给出两个例题应用下吧:
- HDU 1151 Air Raid
- 因为题目明确给出了“It is also known that starting from an intersection and walking through town's streets you can never reach the same intersection i.e."(即:两个路径定不存在交点),所以不需求一次传递闭包,直接用 “最小路径覆盖=总节点数-最大匹配数”即可。
-
#include <iostream> #include <cstdio> #include <cstring> using namespace std; const int kMaxN = 130; struct Edge{ int to, next_; }; int n, m; int edge_size, head[kMaxN]; Edge edge[kMaxN]; bool used[kMaxN]; int linker[kMaxN]; void InitEdge(){ edge_size = 0; memset(head, -1, sizeof(head)); } void AddEdge(int from, int to){ edge[edge_size].to = to; edge[edge_size].next_ = head[from]; head[from] = edge_size++; } void Read(){ scanf("%d %d", &n, &m); InitEdge(); for(int i = 0; i < m; i++){ int u, v; scanf("%d %d", &u, &v); AddEdge(u, v); } } int Dfs(int u){ for(int e_id = head[u]; ~e_id; e_id = edge[e_id].next_){ int v = edge[e_id].to; if(used[v] == false){ used[v] = true; if(linker[v] == -1 || Dfs(linker[v])){ linker[v] = u; return 1; } } } return 0; } void Solve(){ int ret = 0; memset(linker, -1, sizeof(linker)); for(int i = 1; i <= n; i++){ memset(used, false, sizeof(used)); ret += Dfs(i); } printf("%d\n", n - ret); } int main(){ int test; scanf("%d", &test); while(test--){ Read(); Solve(); } return 0; }
- POJ 2594 Treasure Exploration
- 因为题目明确给出了“You should notice that the roads of two different robots may contain some same point.”(即,两个路径是可以相交的),所以必须先求一次闭包,才可以保证“最小路径覆盖=总节点数-最大匹配数”的正确性
-
#include <iostream> #include <cstdio> #include <cstring> using namespace std; const int kMaxN = 510; int n, m; bool G[kMaxN][kMaxN], used[kMaxN]; int linker[kMaxN]; bool Read(){ memset(G, false, sizeof(G)); scanf("%d %d", &n, &m); if(n == 0 && m == 0) return false; for(int i = 0; i < m; i++){ int u, v; scanf("%d %d", &u, &v); G[u][v] = true; } return true; } void Froyd(){ for(int i = 1; i <= n; i++){ for(int j = 1; j <= n; j++){ for(int k = 1; k <= n; k++){ G[i][j] |= (G[i][k] && G[k][j]); } } } } int Dfs(int u){ for(int v = 1; v <= n; v++){ if(G[u][v] && used[v] == false){ used[v] = true; if(linker[v] == -1 || Dfs(linker[v])){ linker[v] = u; return 1; } } } return 0; } int MaxMatch(){ int ret = 0; memset(linker, -1, sizeof(linker)); for(int i = 1; i <= n; i++){ memset(used, false, sizeof(used)); ret += Dfs(i); } return ret; } void Solve(){ Froyd(); printf("%d\n", n - MaxMatch()); } int main(){ while(Read()){ Solve(); } return 0; }