【tarjan缩点】知识点讲解+两道典型例题

用我的话来说,tarjan缩点其实就是把图中的一个强联通分量直接缩成一个点。

【tarjan缩点】知识点讲解+两道典型例题_第1张图片

之后,其实就可以形似一个DAG(有向无环图),大致可以想象成单一有向的线性序列,比如:

 

【tarjan缩点】知识点讲解+两道典型例题_第2张图片

 

性质(结合上面这个DAG图):

①如果超级点入度为0,并且图中只有一个入度为0的超级点,那么显然他可以到达其他任何点,而不能被任何点到达。

②如果超级点出度为0,并且图中只有一个出度为0的超级点,那么显然他可以被任何其他点到达,而不能到达任何点

这些其实也就是做题所用的思路了。

 

那么接下来应该看看如何写tarjan缩点?

首先tarjan算法清楚了(https://blog.csdn.net/m0_38033475/article/details/80069441)之后,其实就是在找到强连通分量要出栈的时候记录一下这是第几个“超级点”:用一个superpoint[i]数组,其值表示i点属于第几个超级点。

板子放到例题代码里。

 

例题1:

【tarjan缩点】知识点讲解+两道典型例题_第3张图片

思路:

这道题的这种传递关系,显然如果把它们的关系画成一个图,那么图中的强连通分量完全可以缩点成超级点,这样就把关系图化成了一个有向无环图。那么根据性质②,得到解题思路。

需要明白:计算出/入度是以“超级点”为单位,即遍历每个点,如果点A所指出的点不属于点A所在的超级点,那么点A就贡献该超级点的出度+1.

代码:

#include
#include
using namespace std;
const int maxn=1e5;
vector v[maxn];
int superpoint[maxn];
stack s;
int dfn[maxn],low[maxn];
int vis[maxn];
int cnt=0;
int sum=0;
void tarjan(int a)
{
	vis[a]=1;
	dfn[a]=low[a]=++cnt;
	s.push(a);
	for(int i=0;i>n>>m;
	while(m--)
	{
		int a,b;
		scanf("%d %d",&a,&b);
		v[a].push_back(b);
	}
	tarjan(1);
	for(int i=1;i<=n;i++)
	{
		if(dfn[i]==0)
			tarjan(i); //这步要记住!因为不一定所有都能由tanrjan(1)可达,有可能有几个点是孤立的小团体~ 
	}
	for(int i=1;i<=n;i++)
	{
		for(int j=0;j1)
		cout<<0;
	else if(temp==1)
		cout<

 

 

例题2:

商业信息共享

 

有 N 个公司,从每个公司都能单向地向另外一个公司分享最新商业信息,因为他们之间有着某种合作,你需要解决两个问题:

  1. 现在有一个最新的商业信息,至少需要告诉多少个公司,使得所有的公司最终都能得到该信息。
  2. 在原有基础上,至少需要再让多少对公司建立这种合作,使任意一个公司获得某个最新商业信息后,经过若干次分享,所有的公司最终都能得到该信息。

输入格式

第一行输入一个整数 N (1≤N≤100)。

接下来 N行,每行若干个整数,表示第 ii 个公司可以向哪些公司分享信息,以 0 结束。

输出格式

输出共两行,每行一个整数,分别表示问题 1 和问题 2 的答案。

样例输入

6
0
6 0
2 0
2 0
3 1 0
0

样例输出

2
2

 

***思路

又是“传递关系”的问题!还是用tarjan缩点做。

对于1小问,其实就是运用性质①,找入度为0的超级点(你自己联想线性序列嘛!其实就是求这里有几坨线性序列)

对于第2小问也是以“超级点”来做的:

需要思考一下,首先你要知道超级点其实就是一个强联通分量(涵盖的点互相可达),那么对于每个超级点,如果既有边指向它,它又可以指出去,如果每个点都这样“开放 既可以get in又可以get out”,那其实就满足题意说的“任意一个点发消息可达所有点”。否则你想想,如果入度为0了,那么这个超级点只能get out,别人访问不了你啊!同理,出度为0,你访问不了别人啊!这两种情况都会不符合题意的。所以我们就需要找到有没有超级点是出/入度为0的,如果你入度为0,那么我需要给你随便连一条边进来好让别人能够get in,如果你出度为0,那么我需要给你随便连一条边出去好让你可以get out。

因为我连一条线可以解决同时解决一个出度为0和一个入度为0的问题,如果只剩出度为0的问题了,就任意给它连出去就好了,因此,max(出度=0的点数,入度=0的点数)即为所求。

 

代码

#include
#include
using namespace std;
const int maxn=1e5;
vector v[maxn];
int superpoint[maxn];
stack s;
int dfn[maxn],low[maxn];
int vis[maxn];
int cnt=0;
int sum=0;
void tarjan(int a)
{
	vis[a]=1;
	dfn[a]=low[a]=++cnt;
	s.push(a);
	for(int i=0;i>n;
	for(int i=1;i<=n;i++)
	{
		int b;
		while(scanf("%d",&b) && b!=0)
		{
			v[i].push_back(b);
		}
	}
	for(int i=1;i<=n;i++)
	{
		if(dfn[i]==0)
			tarjan(i); //这步要记住!因为不一定所有都能由tanrjan(1)可达,有可能有几个点是孤立的小团体~ 
	}
	for(int i=1;i<=n;i++)
	{
		for(int j=0;j

 

 

 

你可能感兴趣的:(蓝桥杯)