TARJAN算法与其运用

一.割点与割边

题目链接 http://hihocoder.com/problemset/problem/1183

割点:

  1. x不是根:只要有low[v[x]]>=low[x] 说明v[x]不通过x没法回到x来时的地方,所以x为割点
  2. x是根:x有两个及以上的直接儿子(注意这里的儿子是放在!dfn中的,不然就是x的儿子的儿子)即可

代码如下:注意点都写注释里了呀!

void tarjan(int x,int fa)
{
	dfn[x]=low[x]=++iindex;int child=0,flag=0;
	for(int i=0;i<v[x].size();i++)
	{
		if(!dfn[v[x][i]])
		{
			child++;//注意child的位置! 
			tarjan(v[x][i],x);	
			low[x]=min(low[x],low[v[x][i]]);
			if(low[v[x][i]]>=dfn[x]) flag=1;//割点条件 
		}
		else if(v[x][i]!=fa) low[x]=min(low[x],dfn[v[x][i]]);//这里要判断v[x][i]不是x的父亲哦 
	}
	if(x==root&&child>=2) cut[x]=1;
	if(x!=root&&flag) cut[x]=1;
	if(cut[x]) tot++;
}

割边:

  1. 与割点的区别:如果v[x]连爸爸都找不到了,那他到爸爸的那条边就是连接他们的唯一路径,就是桥(割边)

所以这里改成:low[v[x]]>dfn[x]

别的都一样啊,不过不用判根

void tarjan2(int x,int fa)
{
	dfn[x]=low[x]=++iindex;
	for(int i=0;i<v[x].size();i++)
	{
		if(!dfn[v[x][i]])
		{
			tarjan2(v[x][i],x);	
			low[x]=min(low[x],low[v[x][i]]);
			if(low[v[x][i]]>dfn[x])
			{
				int t1=min(v[x][i],x);int t2=max(v[x][i],x);
				ans.push_back(make_pair(t1,t2));
			}
		}
		else if(v[x][i]!=fa) low[x]=min(low[x],dfn[v[x][i]]);
	}
}

二.边双

题目链接 http://hihocoder.com/problemset/problem/1184

1.就是做一遍和刚才求割点差不多的程序,但为了存点,我们引入了stack

2.stack中到x的点都属于一个联通分量

代码如下:

void tarjan(int x,int fa)
{
	dfn[x]=low[x]=++iindex;s.push(x);vis[x]=1;
	for(int i=0;i<v[x].size();i++)
	{
		if(!dfn[v[x][i]])
		{
			tarjan(v[x][i],x);
			low[x]=min(low[x],low[v[x][i]]);
		}
		else if(v[x][i]!=fa) low[x]=min(low[x],dfn[v[x][i]]);
	}
	if(low[x]==dfn[x])
	{
		tot++;int minv=1e9;
		while(1)
		{
			int tmp=s.top();minv=min(minv,tmp);
			belong[tmp]=tot;
			s.pop();	
			if(tmp==x) break;
		} 
		num[tot]=minv;
	}
}

三.强联通分量

http://hihocoder.com/problemset/problem/1185

1.与边双的思路很像,但它是有向图

2.所以引入vis的数组以判断这个点在不在stack里。(而边双只是判断他是不是father)

void tarjan(int x,int fa)
{
	dfn[x]=low[x]=++iindex;vis[x]=1;s.push(x);
	for(int i=0;i<v[x].size();i++)
	{
		if(!dfn[v[x][i]])
		{
			tarjan(v[x][i],x);
			low[x]=min(low[x],low[v[x][i]]);
		}
		else if(vis[v[x][i]])//这里和边双不一样 
		{
			low[x]=min(dfn[v[x][i]],low[x]);		
		}
	}
	if(dfn[x]==low[x])
	{
		while(1)
		{
			int tmp=s.top();s.pop();
			vis[tmp]=0;if(tmp!=x) w[x]+=w[tmp];
			belong[tmp]=x;
			if(tmp==x) break;
		}
	}
}

应用

1.缩点:https://www.luogu.org/problemnew/show/P3387

强联通分量的基本应用 本质:有向有环图->有向无环图DAG

2.抢掠计划:https://www.luogu.org/problemnew/show/P3627

求最长路径,一个基本应用呐

注意topo和dfs的应用

有出发点用dfs,否则的用topo

3.信息传递:https://www.luogu.org/problemnew/show/P2661

求最小环,用的强联通分量。不过这个用dfs更清晰简单 vis:0,1,2

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值