洛谷P3388 【模板】割点(tarjan算法)

原题地址:https://www.luogu.org/problemnew/show/P3388

题目背景

割点

题目描述

给出一个nn个点,mm条边的无向图,求图的割点。

输入输出格式

输入格式:

 

第一行输入n,mn,m

下面mm行每行输入x,yx,y表示xx到yy有一条边

 

输出格式:

 

第一行输出割点个数

第二行按照节点编号从小到大输出节点,用空格隔开

 

输入输出样例

输入样例#1: 

6 7
1 2
1 3
1 4
2 5
3 5
4 5
5 6

输出样例#1: 

1 
5

说明

对于全部数据,n \le 20000n≤20000,m \le 100000m≤100000

点的编号均大于00小于等于nn。

tarjan图不一定联通。

思路及代码如下

#include 
#include 
#include 
#include 
#include 
using namespace std;
vector g[20005];
bool vis[20005];
int dfn[20005];
int low[20005];
int cnt;
set ans;   //存割点 
void tarjan(int x, int s)   //s为根节点的下标 
{
	cnt ++;
	dfn[x] = cnt;
	low[x] = cnt;
	int child = 0;
	for(int i = 0; i < g[x].size(); i ++){
		int r = g[x][i];
		if(!vis[r]){                     //第一次访问到r 
			vis[r] = true; 
			tarjan(r, s);
			low[x] = min(low[x], low[r]);    //取最小 
			if(x == s){                     //如果是根节点 
				child ++;        //统计子树数量 
			}
			else {                        
				if(dfn[x] <= low[r])      //如果不是根节点,要能够回到更前面的边 
					ans.insert(x);        //否则此节点就是割点 
			}			
		}
		else {                             //访问过了,dfn[r]肯定小于dfn[x] 
			low[x] = min(low[x], dfn[r]);  //所有取一次min 
		}
	}
	if(child >= 2 && x == s){   //如果是根节点且子树数量大于1 
		ans.insert(x);       //则是割点 
	} 
}
int main()
{
	int n, m;
	while(cin >> n >> m){
		for(int i = 0; i < m; i ++){
			int x, y;
			cin >> x >> y;
			g[x].push_back(y);
			g[y].push_back(x);	
		}
		memset(vis, 0, sizeof(vis));
		for(int i = 1; i <= n; i ++){   //图不一定是连通图 
			cnt = 0;
			if(!vis[i]){		
				vis[i] = true;
				tarjan(i, i);
			}
		}
		cout << ans.size() << endl;
			
		bool first = true;	
		for(auto it = ans.begin(); it != ans.end(); it ++){
			if(first)
				first = false;
			else
				cout << " ";
			cout << *it;
		}
		cout << endl;
		for(int i = 1; i <= n; i ++)
			g[i].clear();
		ans.clear();
	}
	return 0;
} 

 

你可能感兴趣的:(洛谷,图)