在强连通分量的判断中使用BFS

先理清楚概念:与无向图有关的是块,与有向图有关的是强连通分量

强连通分量:分量中任意两点都能相互可达

问题:在有向图中寻找某个顶点所在的的强连通分量,洛谷模板题

输入:n个点,m条边,m行输入 v x

想法:假设要找0所在的强连通分量,可能存在两种性质的点 X:由0点可达的点 Y:能到达0点的点
情形1:若存在可到达0的点,那么从该点开始在反图中进行BFS,遍历到的点都打上Y标记,结束后再从0开始BFS(原图),遍历到的点打上X标记,X和Y重合的点就是与0点在同一强连通分量内的点

情形2:不存在可到达0的点,也就是没有入度,那0点本身就是一个强连通分量

情形3:不存在可从0点出法能到达的点,也就是没有出度,那0点本身就是一个强连通分量

证明:由于强连通分量内的点两两相互可达,存在v和x,若它们相对于0点都具有重合的xy性质,那么存在路径(v, 0)(0, v), (x, 0), (0, x),也就存在(v,0,x)与(x,0,v)两条路径致使x与v相互可达

若将0点换为任意顶点,则可以查找所有的强连通分量

此方法部分受 Kosaraju 算法的启发,算是我想了小半个月的结果,但是测试下来

image
洛谷强连通分量链接:https://www.luogu.com.cn/problem/B3609
这是ACcode,或许可以在很多类似的题力使用,再修改一下就可以判定动态图的连通性了

点击查看代码
#include <iostream>
#include <queue>
#include <vector>
#include <algorithm> 
#include <fstream>
#include <sstream>
#define typedef
#define max 100001
using namespace std;

typedef struct edge
{
	int v;
	int x;
};

typedef struct vertex
{
	int v = 1;
	vector <int> in_index;
	vector <int> out_index;
	int x;
	int y = 0;
	//int b = 1;
};


void out(int c)
{
	cout << c << ' ';
}
int main()
{
	vector <vector<int>> s;
	s.resize(1000);
	
	int cnt = 0;
	int n,m;
	cin >> n >> m;
	edge edge[1000]; vertex ver[1000];
	for (int i = 1; i <= m; ++i)
	{
		cin >> edge[i].v >> edge[i].x;
		ver[edge[i].v].out_index.push_back(i);
		ver[edge[i].x].in_index.push_back(i);
	}
	
	queue <int> s_y;
	ver[1].x = -1; //start from 1 as a root
	for (int i = 1; i <= m; ++i)
	{
		if (edge[i].x == 1)
		{
			ver[edge[i].v].y = 1;//瀵绘壘鍏锋湁y鎬ц川鐨勭偣
			s_y.push(edge[i].v);
		}
		
	}
	queue <int> Break;//鐢ㄦ潵鍌ㄥ瓨鏂偣
	for (int i = 1; i <= n; ++i)
	{
		Break.push(i);
	}
	//浠庡叿鏈墆鎬ц川鐨勭偣寮€濮嬪湪鍙嶅浘涓墿鏁?
	
	do
	{
		
		int d = Break.front();
		Break.pop();
		if (ver[d].v)
		{
			vector <int> stack;
			for (int i = 0; i < ver[d].in_index.size(); ++i)
			{
				s_y.push(edge[ver[d].in_index[i]].v);
			}
			stack.push_back(d);
			ver[d].y = d;   //自身具有Y性质 
			ver[d].v = 0;   //已在分量中 
			if (s_y.empty())
			{
				cnt++;
				for(int i = 0;i <stack.size();++i)
			{
				//cout << stack[i] << ' ';
				s[cnt].push_back(stack[i]);	
			}
				continue;
			}
			while(!s_y.empty())
			{	
			int find = s_y.front();
			s_y.pop();
			ver[find].y = d;
			//stack.push_back(find);
			int l = ver[find].in_index.size();
			for (int i = 0; i < l;++i)
			{
				int b = edge[ver[find].in_index[i]].v;
				//cout << b << endl;
				if (ver[b].y == d || !ver[b].v)
				{
					continue;
				}
				ver[edge[ver[find].in_index[i]].v].y = d;
				
				//cout << b << endl;
				s_y.push(b);	
			}
			
			}
			
			queue <int> search;
			search.push(d);
			do
			{
				int find_x = search.front();
				search.pop();
				int l = ver[find_x].out_index.size();
				
				for(int i = 0;i < l;++i)
				{
					int b = edge[ver[find_x].out_index[i]].x;
					if (ver[b].x == d) continue;
					ver[b].x = d;
					//cout << b;
					/*if (ver[b].x != ver[b].y && ver[b].b)
					{
						Break.push(b);
						ver[b].b = 0;
					}else */if(/*ver[b].b * */ver[b].v && ver[b].x == ver[b].y){
						ver[b].v = 0;
						stack.push_back(b);
						search.push(b);
					}
					
				}
					
			}while(!search.empty());
			cnt++;
			//cout << cnt << "  ";
			sort(stack.begin(), stack.end());
			for(int i = 0;i <stack.size();++i)
			{
				//cout << stack[i] << ' ';
				s[cnt].push_back(stack[i]);	
			}
			
			//cout << endl;
			
			/*for(int i = 0;!Break.empty();++i)
			{
				int c = Break.front();
				out(c);
				Break.pop();	
			}*/
		}
		
	}while(!Break.empty());
	cout << cnt;
	cout << endl;
	for (int i = 1;i <= cnt; ++i)
	{
		for (int j = 0;j < s[i].size(); ++j)
		{
			cout << s[i][j] <<' ';
		}
		cout << endl;
	}
}
	
	 


代码可以通过网站的测试,可以保证正确性,但是这个算法仍然比不过经典算法,不管是在时间上还是在空间上,这算是个比较失败的算法,不过或许可以用GPU加速呢

之前的代码写的太差了,现在用AI了一下,逻辑更清晰了,核心思想就是两种性质的扩散

<details>
<summary>点击查看代码</summary>

```
include <iostream>
include <queue>
include <vector>
include <algorithm>
using namespace std;

struct Edge {
    int to;
    int from;
};

struct Vertex {
    bool active = true;
    vector<int> in_edges;
    vector<int> out_edges;
    int x_mark = -1;
    int y_mark = -1;
};

void optimized_scc(vector<Vertex>& vertices, vector<Edge>& edges) {
    int n = vertices.size() - 1; // 顶点从1开始编号
    vector<vector<int>> components;
    queue<int> pending_vertices;

    // 初始化待处理队列
    for (int i = 1; i <= n; ++i) {
        if (verticesi.active) {
            pending_vertices.push(i);
        }
    }

    while (!pending_vertices.empty()) {
        int v = pending_vertices.front();
        pending_vertices.pop();

        if (!verticesv.active) continue;

        // 阶段1:传播y标记
        queue<int> y_queue;
        verticesv.y_mark = v;
        y_queue.push(v);

        while (!y_queue.empty()) {
            int current = y_queue.front();
            y_queue.pop();

            for (int e : verticescurrent.in_edges) {
                int u = edgese.from;
                if (verticesu.y_mark != v && verticesu.active) {
                    verticesu.y_mark = v;
                    y_queue.push(u);
                }
            }
        }

        // 阶段2:传播x标记并收集强连通分量
        vector<int> component;
        queue<int> x_queue;
        verticesv.x_mark = v;
        x_queue.push(v);
        component.push_back(v);
        verticesv.active = false;

        while (!x_queue.empty()) {
            int current = x_queue.front();
            x_queue.pop();

            for (int e : verticescurrent.out_edges) {
                int u = edgese.to;
                if (verticesu.x_mark != v && verticesu.active) {
                    if (verticesu.y_mark == v) {
                        verticesu.x_mark = v;
                        component.push_back(u);
                        verticesu.active = false;
                        x_queue.push(u);
                    }
                }
            }
        }

        // 保存找到的强连通分量
        if (!component.empty()) {
            sort(component.begin(), component.end());
            components.push_back(component);
        }
    }

    // 输出结果
    cout << components.size() << endl;
    for (auto& comp : components) {
        for (int v : comp) {
            cout << v << ' ';
        }
        cout << endl;
    }
}

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

    int n, m;
    cin >> n >> m;

    vector<Vertex> vertices(n + 1);
    vector<Edge> edges(m + 1);

    for (int i = 1; i <= m; ++i) {
        int u, v;
        cin >> u >> v;
        edgesi = {v, u};
        verticesu.out_edges.push_back(i);
        verticesv.in_edges.push_back(i);
    }

    optimized_scc(vertices, edges);
    return 0;
}
```
</details>
posted @ 2025-02-19 17:05  hendry_0702  阅读(51)  评论(0)    收藏  举报