最小环

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 度度熊保护村庄

计算几何+最小环。

posted @ 2020-07-13 16:09  JiaZP  阅读(255)  评论(0编辑  收藏  举报