• 博客园logo
  • 会员
  • 众包
  • 新闻
  • 博问
  • 闪存
  • 赞助商
  • HarmonyOS
  • Chat2DB
    • 搜索
      所有博客
    • 搜索
      当前博客
  • 写随笔 我的博客 短消息 简洁模式
    用户头像
    我的博客 我的园子 账号设置 会员中心 简洁模式 ... 退出登录
    注册 登录
nannandbk
博客园    首页    新随笔    联系   管理    订阅  订阅
[图论]判环的几种方法

判环的几种方法

拓扑排序判环

  • 对于有向图:
//有向图环判断
#include<bits/stdc++.h>
using namespace std;

vector<int>edge[10001];
int n,m,d[10001];
queue<int>q;
inline void TopoSort()
{
	int cnt = 0;
	for(int i = 1;i<=n;i++)
	{
		if(!d[i])
		{
			q.push(i);
			++cnt;
		}
	}

	while(!q.empty())
	{
		int x = q.front();
		q.pop();
		for(auto y:edge[x])
			if(--d[y]==0)
			{
				q.push(y);
				++cnt;
			}
	}
	if(cnt==n)cout<<"No\n";
	else cout<<"Yes\n";
}

int main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	cin>>n>>m;
	for(int i =1 ;i<=m;i++)
	{
		int x,y;
		cin>>x>>y;
		edge[x].push_back(y);
		++d[y];
	}
	TopoSort();
	return 0;
}
/*
4 4
1 2
2 3
3 4
1 4

No
*/
  • 对于无向图

因为是双向边,那么我们需要先把度数为\(1\)的点加入(区别与上面有向图(度数为0的加入))队列,

 for(int i = 1; i <= n; i++)
        if(d[i] == 1)
            q.push(i),vis2[i] = true;

然后当]之前没有访问过且减去和上一个点所连的边带来的影响之后度数是不是小于等于1,是的话加入队列里面。

  while(!q.empty())
    {
        int u = q.front();  q.pop();
        for(auto v : e[u])
            if(--d[v] <= 1 && !vis2[v])
                q.push(v),vis2[v] = true;
    } 

最后度数大于\(1\)的点在环上

板子:

// AC one more times
// nndbk
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod = 1e9 + 7;
const int N = 2e5 + 10;
int n,d[N];
vector<int> e[N];
bool vis[N];

void toposort()
{
    queue<int> q;
    for(int i = 1;i <= n; i++)
        vis[i] = 0;
    for(int i = 1; i <= n; i++)
        if(d[i] == 1)
            q.push(i),vis[i] = true;
    while(!q.empty())
    {
        int u = q.front();  q.pop();
        
        for(auto v : e[u])
            if(--d[v] <= 1 && !vis[v])
                q.push(v),vis[v] = true;
    }   
}



int main()
{
    ios::sync_with_stdio(false);   cin.tie(nullptr), cout.tie(nullptr);

    cin>>n;
    for(int i = 1; i <= n; i++)
    {
        int u , v;    cin>>u>>v;
        e[u].push_back(v);
        e[v].push_back(u);
        d[u]++;
        d[v]++;
    }
    toposort();
    set<int>s;
    for(int i = 1;i <= n; i++)
        if(d[i]>1)s.insert(i);
    for(auto x : s)
        cout<<x<<" ";
    cout<<"\n";
    return 0;
}

例题:H. Mad City

// AC one more times
// nndbk
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod = 1e9 + 7;
const int N = 2e5 + 10;
int n,a,b,d[N];
bool vis[N];
ll dist1[N],dist2[N];
vector<int> e[N];
bool vis2[N];
int tot,l[N];
void toposort()
{
    queue<int> q;
    tot = 0;
    for(int i = 1;i <= n; i++)
        vis2[i] = 0;
    for(int i = 1; i <= n; i++)
        if(d[i] == 1)
            q.push(i),vis2[i] = true;
    while(!q.empty())
    {
        int u = q.front();  q.pop();
        //l[++tot] = u;
       // cout<<"u = "<<u<<"\n";
        for(auto v : e[u])
            if(--d[v] <= 1 && !vis2[v])
                q.push(v),vis2[v] = true;
    }   

    // for(int i = 1;i <= tot; i++)
    //     cout<<l[i]<<" ";
    // cout<<"\n";
}



int main()
{
    ios::sync_with_stdio(false);   cin.tie(nullptr), cout.tie(nullptr);
    int t;
    cin>>t;
    while(t--)
    {
        cin>>n>>a>>b;

        for(int i = 1;i <= n; i++)
            e[i].clear(),dist1[i] = 0,dist2[i] = 0,d[i] = 0;
        for(int i = 1; i <= n; i++)
        {
            int x , y;    cin>>x>>y;
            e[x].push_back(y);
            e[y].push_back(x);
            d[y]++;
            d[x]++;
        }
        toposort();
        queue<pair<int,ll>>q;
        q.push({a,0});
        dist1[a] = 0;
        memset(vis,false,sizeof(vis));
        vis[a] = true;

        while(!q.empty())
        {
            auto i = q.front();
            q.pop();
            int u = i.first,dis = i.second;
            for(auto v : e[u])
            {
                if(!vis[v])
                {
                    vis[v] = true;
                    dist1[v] = dis + 1;
                    q.push({v,dist1[v]});
                }
            }
        }
        
        q.push({b,0});
        dist2[b] = 0;
        memset(vis,false,sizeof(vis));
        vis[b] = true;

        while(!q.empty())
        {
            auto i = q.front();
            q.pop();
            int u = i.first,dis = i.second;
            for(auto v : e[u])
            {
                if(!vis[v])
                {
                    vis[v] = true;
                    dist2[v] = dis + 1;
                    q.push({v,dist2[v]});
                }
            }
        }
        bool ok = false;
        // for(int i = 1;i <= n;i++)
        //     cout<<d[i]<<" ";
        // cout<<"\n";
        //  for(int i = 1;i <= n;i++)
        //     cout<<dist1[i]<<" ";
        // cout<<"\n";
        //  for(int i = 1;i <= n;i++)
        //     cout<<dist2[i]<<" ";
        // cout<<"\n";
        for(int i = 1;i <= n;i++)
        {
            if(d[i]>1&&dist1[i]>dist2[i])ok = true;
        }
        if(ok)cout<<"YES\n";
        else cout<<"NO\n";
    }
    return 0;
}

dfs判环

\(vis\)代表每个结点的状态,\(0\)代表还没被访问,\(1\)代表正在被访问,\(2\)代表访问结束
如果一个状态为\(1\)的结点,与他相连的结点状态也为\(1\)的话就代表有环,这个可以用\(dfs\)实现

// AC one more times
// nndbk
#include <bits/stdc++.h>
using namespace std;
 
const int N = 1e5 + 10;
vector <int> e[N];
int vis[N];
int n, m;
bool dfs(int u) {
    vis[u] = 1; // 1表示正在访问
    for (int v: e[u]) {
        if (vis[v] == 1) { //如果正在访问的点又被访问到则代表有环
            return false;
        } else if (vis[v] == 0) { // 0代表还没有访问
            if (!dfs(v)) {
                return false;
            }
        }
    }
    vis[u] = 2; // 2代表访问结束
    return true;
}

bool check(){
    for (int i = 1; i <= n; i++) {
            if (vis[i] == 0) {
                if (!dfs(i)) {
                    return false;
                }
            }
        }
        return true;
    };

int main()
{

    cin >> n >> m;
    for (int i = 0; i < m; i++) {
        int u,v;  cin >> u >> v;
        e[u].push_back(u);
    }
    memset(vis, 0, sizeof(vis));
    
    puts(check() ? "no have" : "have");
    return 0;
}

dfs找环数和找路径

#include<bits/stdc++.h>
using namespace std;
const int N = 1e6;


int nums = 0;
int vis[N],pre[N];

vector<int>e[N];
void dfs(int u,int fa){     //dfs
    vis[u] = 1;
    for(auto v : e[u]){  
        if(v==fa)   continue;   //如果是无向的 a-->b 的同时也有 b-->a,所以直接排除另外的一种情况
        if(vis[v]==0){          //如果没有访问就标记当前元素的前一个元素
            pre[v] = u;
            dfs(v,u);           //并且一直递归访问下去
        }else if(vis[v]==1){
            int tmp = u,cnt = 1;//环的长度
            while(tmp != v)
            {
                cout<<tmp<<" ";
                cnt++;
                tmp = pre[tmp];
            }
            cout<<v<<"\n";
            nums++;
        }
    }
    vis[u] = 2;
}
int main()
{
    int n;int u,v;        //n为点数  m为边数    u是起点v是终点
    cin >> n;
    for(int i = 1;i <= n;i++){
        cin >> u >> v;
        e[u].push_back(v);
        e[v].push_back(u);
    }

    for(int i = 1;i <= n;i++){      //可能是非联通图
        if(vis[i]==0)               //每一次可以访问完和该点相连的所有点
            dfs(i,-1);
    }
    cout << nums << endl;           //环数
    return 0;
}

/*
3 3
2 1
3 2
1 3
*/
posted on 2023-10-01 20:52  nannandbk  阅读(1149)  评论(2)    收藏  举报
刷新页面返回顶部
博客园  ©  2004-2025
浙公网安备 33010602011771号 浙ICP备2021040463号-3