割点与割边

割点

算法流程

#include <bits/stdc++.h>
using namespace std;

int n,m;
vector <int> e[20001];
int dfn[20001],low[20001],timer;
bool gd[20001];
int ans;

void tarjan(int x,int fa)
{
	dfn[x] = low[x] = ++timer;
	int child = 0;
	for (int i = 0; i < e[x].size(); i++)
	{
		if(!dfn[e[x][i]])
		{
			tarjan(e[x][i],fa);
			low[x] = min(low[x],low[e[x][i]]);
			if(low[e[x][i]] >= dfn[x] && x != fa)//不是根节点
			{
				gd[x] = true;
			}
			if(x == fa)//根节点
			{
				child++;
			}
		}
		low[x] = min(low[x],dfn[e[x][i]]);		
	}
	if(child >= 2)
	{
		gd[x] = true;
	}
}

int main()
{
	cin >> n >> m;
	int a,b;
	for (int i = 1; i <= m; i++)
	{
		cin >> a >> b;
		e[a].push_back(b),e[b].push_back(a);
	}
	for (int i = 1; i <= n; i++)
	{
		if(!dfn[i])
		{
			tarjan(i,i);
		}
	}
	for (int i = 1; i <= n; i++)
	{
		if(gd[i])ans++;
	}
	cout << ans << endl;
	for (int i = 1; i <= n; i++)
	{
		if(gd[i])cout << i << " ";
	}
	return 0;
}

类似于tarjan求scc

判定

每次选取一个点作为根节点,判断割点分为根节点与非根节点的判断:

根节点:只需要独立的子树(当遍历其它子树时,不会遍历到当前子树,即不连通,就相当于是独立了)大于两个即可判定为割点

非根节点:其下的子节点存在一个的low值大于等于当前节点dfn值,即最多遍历到当前节点,不能连到祖先或者旁系了,也就相当于是局部独立了

fa表示根节点

与tarjan区别:

tarjan中的min值(low,dfn)dfn貌似是可以改为low的(待定,本人未证明)
而这里不行,这里是靠dfn来判断属于哪一颗子树的,因为算法实际上是假设了把当前节点删去后,判断连通性,若直接用low的话,相当于时无视了假设,边与点仍然存在,并且帮助转移,所以不行

这里else要不要无所谓。

割边

算法流程

struct node {int to,nxt; } e[M * 2];
int hd[N],nE = 1;

void add(int u,int v)
{
    e[++nE] = (node){ v,hd[u]};
    hd[u] = nE;
}

int dfn[20001],low[20001],timer;
int ans;

void tarjan(int x,int fa)
{
    dfn[x] = low[x] = ++timer;
    for (int i = hd[x]; i; i = e[i].nxt)
    {
        int v = e[i].to;
        if(!dfn[v])
        {
            tarjan(v,i ^ 1);//传反向边
            low[x] = min(low[x],low[v]);
            if(low[v] > dfn[x])
            {
                ans++;
            }
        }
        else if(fa != i)  low[x] = min(low[x],dfn[v]);		
    }
}

int main1()
{
    int a,b;
    for (int i = 1; i <= m; i++)
    {
        a = data[i][0],b = data[i][1];
        add(a,b),add(b,a);
    }
    for (int i = 1; i <= n; i++)
    {
        if(!dfn[i])
        {
            tarjan(i,i);
        }
    }
    printf("%d ",ans);
    return 0;
}

(尚未验证,大概是对的,慎用)
思想差不多,但简化了很多,最不同的地方在于 >= 变成 >,意为连父节点都到不了了

与割点有两点不同:
① 对于节点 i 连向子节点 j 的边,如果 j 所能到达的最小时间戳值都大于(不能等于,这是不同之处) i 的时间戳时,才判定此边为割边,不能等于意味着不通过这条边,子节点连 i 号点本身也到不了,这样,如果此边被割去,j 号点也就被割开了。
② 在更新能到达的最小时间戳时,需要跳过反向边。

posted @ 2022-07-13 19:57  此间无物  阅读(29)  评论(0)    收藏  举报