最小环
oi-wiki上讲得极好:(链接)
问题
图上找一大小至少为3的环,要求最小化环上边权和。
解法
Dijkstra
枚举环上一条边 \((u, v)\),删掉它,然后跑Dij,用距离 + \(val[u][v]\),取最小值即可。
复杂度:\(O(m(n+m)logn)\)
Floyd
如果 \(m = n^2\),那么Dij就会造成 \(O(n^4logn)\) 的复杂度。因此我们需要一个更跟 \(n\) 有关的算法。
还是上 oi-wiki 吧。
记原图中 u, v 之间边的边权为 val(u, v) 。
我们注意到 Floyd 算法有一个性质:在最外层循环到点 k 时(尚未开始第 k 次循环),最短路数组 dis 中, dis[u][v] 表示的是从 u 到 v 且仅经过编号在 [1,k) 区间中的点的最短路。
由最小环的定义可知其至少有三个顶点,设其中编号最大的顶点为 w ,环上与 w 相邻两侧的两个点为 u, v ,则在最外层循环枚举到 k = w 时,该环的长度即为 dis[u][v] + val[w][u] + val[v][w]。
故在循环时对于每个 k 枚举满足 i < k, j < k 的 (i, j) ,更新答案即可。
时间复杂度: O(n^3)
附上我的代码。(有向图边权为1的最小环)
for (register int i = 1; i <= m; ++i) {
for (register int j = 1; j <= m; ++j) {
if (e[i][j]) dis[i][j] = 1;
else dis[i][j] = inf;
}
}
int ans = inf;
for (register int k = 1; k <= m; ++k) {
for (register int i = 1; i < k; ++i) {
for (register int j = 1; j < k; ++j) if (j != i) {
if (e[k][i] && e[j][k]) {
MIN(ans, dis[i][j] + 2);
}
}
}
for (register int i = 1; i <= m; ++i) if (dis[i][k] < inf) {
for (register int j = 1; j <= m; ++j) if (j != i) {
MIN(dis[i][j], dis[i][k] + dis[k][j]);
}
}
}
例题:
hdu 6080 度度熊保护村庄
计算几何+最小环。