## [【$HDU1151$】—$Air$ $Raid$(最小路径覆盖)](http://acm.hdu.edu.cn/showproblem.php?pid=1151) - 题解描述 给定一个$DAG$(有向无环图),选定最少的点,使得从这些点出发可以覆盖每一条路径(即每个点都经过至少一遍)。 **输入**: ```cpp {.line-numbers} 2 4 3 3 4 1 3 2 3 3 3 1 3 1 2 2 3 ``` **输出** ```cpp {.line-numbers} 2 1 ``` ![](https://dsideal.obs.cn-north-1.myhuaweicloud.com/HuangHai/BlogImages/%7Byear%7D/%7Bmonth%7D/%7Bmd5%7D.%7BextName%7D/20230731111020.png) 以测试数据为例,$4$个路口,$3$条路。现派伞兵经过所有路口,求最少要派几名。 **思路**: 首先构建二分图,图的左边代表$1-n$,右边也代表$1'-n'$,若两点$i->j'$可行,则二分图中建边$i->j'$,求最少路径覆盖即为求最大独立集,也就是$n-$最大匹配数。 >**解释**: 在二分图中,最小路径覆盖和最大独立集是等价的。 二分图是指一个图的顶点可以分为两个不相交的集合,并且图中的每条边都连接一个集合中的顶点和另一个集合中的顶点。 在二分图中,最小路径覆盖的解可以直接对应到最大独立集的解,反之亦然。具体来说,对于二分图中的最小路径覆盖问题,我们可以将其转化为最大独立集问题求解。而对于二分图中的最大独立集问题,我们也可以将其转化为最小路径覆盖问题求解。 这个等价关系的原因是,二分图的最大独立集正好对应着最小路径覆盖中选择的路径的起点和终点集合。因为在二分图中,任意两个相邻的顶点之间都没有边相连,所以选择一个顶点就意味着不选择与之相邻的顶点。 结论:二分图中最少路径覆盖即为最大独立集 ```cpp {.line-numbers} #include using namespace std; const int N = 1e3 + 5; /* 测试用例: 2 4 3 3 4 1 3 2 3 3 3 1 3 1 2 2 3 答案: 2 1 */ int match[N]; int st[N], g[N][N]; int n, m; int dfs(int x) { for (int i = 1; i <= n; i++) { if (g[x][i] && !st[i]) { st[i] = 1; int t = match[i]; if (t == -1 || dfs(t)) { match[i] = x; return 1; } } } return 0; } int main() { int T; cin >> T; // T组测试数据 while (T--) { cin >> n >> m; // n个节点,m条边 // 多组测试数据 memset(match, -1, sizeof match); memset(g, 0, sizeof g); for (int i = 0; i < m; i++) { int a, b; cin >> a >> b; g[a][b] = 1; // a->b,有向图 } // 如果a->b,b->c,则 a->c,题意中说如果存在传递关系,需要我们建立关系清晰的边,也就是, // 用 floyd,在O(N^3)的复杂度下完善点点之间的边关系 for (int k = 1; k <= n; k++) for (int i = 1; i <= n; i++) for (int j = 1; j <= n; j++) g[i][j] |= g[i][k] & g[k][j]; // 新图建成,开始跑匈牙利算法,求二分图的最大匹配 int cnt = 0; for (int i = 1; i <= n; i++) { memset(st, 0, sizeof st); if (dfs(i)) cnt++; } // 二分图的最小点覆盖 = n- 二分图的最大匹配 printf("%d\n", n - cnt); } return 0; } ```