图论基础之 图中找环

对于有向图而言 可以使用拓扑排序的方式找出图中的环

#include
using namespace std;
const int maxn = 1e5+7;
int n;
int du[maxn];//入度
vector gra[10];
vector res;
void addedge(int a,int b){
    du[b]++;
    gra[a].push_back(b);
}
void topo(){
    queue q;
    for(int i = 1; i <= n; i++){
        if(du[i] == 0){
            q.push(i);
        }
    }
    while(!q.empty()){
        int temp = q.front();
        q.pop();
        for(int i = 0; i < gra[temp].size(); i++){
            int to = gra[temp][i];
            du[to]--;
            if(!du[to]){
                q.push(i);
            }
        }
    }
}
int main(){
    cin>>n;
    for(int i = 0; i < n; i++){
        int a,b;
        cin>>a>>b;
        addedge(a,b);
    }
    topo();
    for(int i = 1; i <= n; i++){
        if(du[i]){
            cout<

对于无向图 可以采用并查集的方式,在读边的时候,如果两个顶点在同一个集合中,说明构成了环,这时令这两个顶点作为起点和终点,深搜一下输出路径即可

蓝桥杯2017国赛c++b组 t4 找环

题意:

编号为1nn个点,以及n-1条边构成一棵树。现在在树上加上一条边,这样就构成了一个含环的图了。请你找出该环上的结点,从小到大输出这些结点编号。

#include
using namespace std;
const int maxn = 1e+7;
int n;
int p[maxn];
vector gra[10];
vector res;
int init(){
    memset(p,0,sizeof(p));
    for(int i = 1; i <= n; i++){
        p[i] = i;
    }
}
int find(int x){
    return p[x]==x?x:p[x]=find(p[x]);
}
int unit(int x,int y){
    p[x] = y;
}
void addedge(int x,int y){
    gra[x].push_back(y);
    gra[y].push_back(x);
}
int s,e;
int vis[maxn];
int path[maxn];
void dfs(int x){
    cout<>n;
    init();
    memset(vis,0,sizeof(vis));
    memset(path,0,sizeof(path));
    for(int i = 0; i < n; i++){
        int a,b;
        cin>>a>>b;
        int aa = find(a),bb = find(b);
        if(aa!=bb){
            unit(a,b);
        }else{// 说明构成了环
            s = a,e = b;
            break;
        }
        addedge(a,b);
    }
    vis[s] = 1;
    dfs(s);
    int temp = e;
    res.push_back(temp);
    while(path[temp]){
        res.push_back(path[temp]);
        temp = path[temp];
    }
    sort(res.begin(),res.end());
    for(int i = 0; i < res.size(); i++){
        if(i>0){
            cout<<' ';
        }
        cout<

另外还可以采用深搜的方式,首先需要了解

在图的遍历中,往往设置了一个标记数组vis来记录顶点是否被访问过。但有些时候需要改变vis值的意义。令vis具有3种值并表示3种不同含义

vis = 0,表示该顶点没没有被访问
vis = 1,表示该顶点已经被访问,但其子孙后代还没被访问完,也就没从该点返回
vis = 2,,表示该顶点已经被访问,其子孙后代也已经访问完,也已经从该顶点返回
可以vis的3种值表示的是一种顺序关系和时间关系

《算法导论》334页有这4种边的准确定义

图论基础之 图中找环_第1张图片

DFS过程中,对于一条边u->v
vis[v] = 0,说明v还没被访问,v是首次被发现,u->v是一条树边
vis[v] = 1,说明v已经被访问,但其子孙后代还没有被访问完(正在访问中),而u又指向v,说明u就是v的子孙后代,u->v是一条后向边,因此后向边又称返祖边,
vis[v] = 2,说明v已经被访问,其子孙后代也已经全部访问完,u->v这条边可能是一条横叉边,或者前向边

注意:树边,后向边,前向边,都有祖先,后裔的关系,但横叉边没有,u->v为横叉边,说明在这棵DFS树中,它们不是祖先后裔的关系它们可能是兄弟关系,堂兄弟关系,甚至更远的关系,如果是dfs森林的话,u和v甚至可以在不同的树上

对于无向图而言,只存在树边和返祖边,当发现一条返祖边时,说明找到了环,这时输出所有vis[i] = 1的节点即可

#include
using namespace std;
const int maxn = 1e5+7;
int vis[maxn];
int n;
vector gra[10];
vector res;
void addedge(int a,int b){
    gra[a].push_back(b);
    gra[b].push_back(a);
}
int flag = 0;
void findp(){  // 找环
    for(int i = 1; i <= n; i++){
        if(vis[i] == 1){
            res.push_back(i);
        }
    }
}
void dfs(int x,int p){
    for(int i = 0; i < gra[x].size(); i++){
        int to = gra[x][i];
        if(!vis[to]){
            vis[to] = 1;
            dfs(to,x);
        }else if(to!= p && vis[to]){
            if(!flag){
                flag = 1;
                findp();
            }
            return;
        }
    }
    vis[x] = 2;
}
int main(){
    cin>>n;
    memset(vis,0,sizeof(vis));
    for(int i = 0; i < n; i++){
        int a,b;
        cin>>a>>b;
        addedge(a,b);
    }
    vis[1] = 1;
    dfs(1,0);
    sort(res.begin(),res.end());
    for(int i = 0; i < res.size(); i++){
        if(i>0){
            cout<<' ';
        }
        cout<


你可能感兴趣的:(图论)