炸铁路 (图论求割边)

题目链接:P1656
图论中有一类题要求割点或者割边,首先说定义:
无向图中:

割点:去掉这个点之后,整个图被分成了两部分。

割边:去掉这个边之后,整个图被分成了两部分。

那么如何求割点和割边呢。
对于一个节点,如果它不能通过某个路径,回到它的祖先,则这个点是割点。
对于一个边,如果它的子端点,不能通过某条路径,回到它的父端点,则这条边是割边。
对于以上算法,可以使用dfs来实现,对每一个节点加上一个时间戳,来表示递归访问的顺序,然后设置一个low数组,用来表示节点可以访问到的最远的祖先节点,并且在回溯的时候对每一个节点进行更新。一边更新一边判断low是否大于时间戳dfn,如果是大于等于则求割点,大于则求割边。
以下是AC代码:

#include
using namespace std;
vectornumbers[200];
int low[200];
struct node
{
	int aa;
	int bb;
};
bool cmp(node a, node b)
{
	if (a.aa != b.aa)return a.aa < b.aa;
	else return a.bb < b.bb;
}
node ans[100000];
int tt = 0;
int dfn = 0;
int num[200];
void dfs(int son,int fu)
{
	num[son] =++dfn;
	low[son] = num[son];
	for (int i = 0; i < numbers[son].size(); i++)
	{
		int t = numbers[son][i];
		if (num[t] && t != fu)low[son] = min(low[son], num[t]);
		if (!num[t])
		{
			dfs(t, son);
			if (num[son] < low[t])
			{
				ans[tt].aa= min(son, t);
				ans[tt].bb = max(son, t);
				tt++;
			}
			//low[son] = min(low[son], low[t]);
		}
	}
	low[fu] = min(low[son], low[fu]);
}
int main()
{
	int a, b; cin >> a >> b;
	for (int i = 0; i < b; i++)
	{
		int x, y; cin >> x >> y;
		numbers[x].push_back(y);
		numbers[y].push_back(x);
	}
	for (int i = 1; i <= a; i++)if (!num[i])dfs(i, i);
	//for (int i = 1; i <= a; i++)cout << num[i] << " " << low[i] << endl;
	sort(ans, ans + tt, cmp);
	for (int i = 0; i < tt; i++)cout << ans[i].aa << " " << ans[i].bb << endl;
}

你可能感兴趣的:(个人题解,算法,数据结构)