在强连通分量的判断中使用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 算法的启发,算是我想了小半个月的结果,但是测试下来

洛谷强连通分量链接: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>

浙公网安备 33010602011771号