图中环学习指南

无向图求最大环长度

/*
时间戳+dfs->求最大环的长度 (无向图)

*/
const int N=2e5+10;
//b数组:找出每个连通块的最大环,
//dfn数组:为每个节点打上时间戳,演变为一颗深度优先搜索树
int tot,b[N],dfn[N];
bool vis[N];
vector<int> e[N];
int n;
void dfs(int u,int cnt){
    //cnt-dfn[u]为环的大小
    if(vis[u]) {b[tot]=max(b[tot],cnt-dfn[u]);return;}
    dfn[u]=cnt;
    vis[u]=1;
    for(auto v:e[u]) dfs(v,cnt+1);
}
int main() {
    for(int i=1;i<=n;i++){
        if(!vis[i]) dfs(i,0);
    }
    return 0;
}

无向图最小环

[problem description]

给定一张无向图,求图中一个至少包含 3 个点的环,环上的节点不重复,并且环上的边的长度之和最小。该问题称为无向图的最小环问题。在本题中,你需要输出最小环的方案,若最小环不唯一,输出任意一个均可。若无解,输出 No solution.图的节点数不超过 100

[input]

第一行两个正整数 n,m 表示点数和边数。
接下来 m 行,每行三个正整数 x,y,z,表示节点 x,y 之间有一条长度为 z 的边。

[output]

输出一个最小环的方案:按环上顺序输出最小环上的点。若最小环不唯一,输出任意一个均可。若无解,输出 No solution.

[solved]

根据n的值小于100,可以想到可能用到Floyd算法,用Floyd算法算最短路,Floyd算法中的d[i][j]表示经过编号小于k的结点,i到j的最短距离。所以就会萌生出来一个想法,在Floyd算法中最外层k循环中,我们每次就把k当作中间的那个点,i是开头,j是结尾,整个环的权值和就是d[i][j]+a[i][k]+a[k][j],每次都把这个结果与ans作比较,取更小的那一个环。

最外层循环k,每次先循环i和j找以k为中间点的最小环,如果比记录的答案要小就记录当前路径。之后Floyd,更新经过k的最短路。注意:为什么每次循环k都是先找最小环,然后再更新,而不是先更新再找最小环,或者先Floyd一遍,之后再开始找最小环。原因:因为如果先找经过k的最短路,然后再找以k为中间点的最小环的话,会出现重边问题。

[证明]

讨论一下这种方法的正确性,每次循环k,我们d[i][j]中存的都是不经过k的i和j之间的最短路,之后我们再跑Floyd更新所有经过k结点的最短距离,为下次k+1循环做准备,下次同理,这样就可以保证每个节点都可以尝试做中间结点。而本道题要求求出环上的结点,我们可以用一个vector数组存储路径,每次Floyd更新最小值,我们新建一个pos数组,存储i到j之间最短距离的中间结点,然后通过递归查询出所有结点。

posted @ 2023-10-16 20:50  White_Sheep  阅读(13)  评论(0编辑  收藏  举报