首页 > 其他分享 >强连通分量-缩点

强连通分量-缩点

时间:2024-07-28 16:40:12浏览次数:12  
标签:缩点 连通 int low inf now 节点 分量

初学只需要背代码就好了,而复习的时候要考虑的就多了(

(打牛客的时候用到,发现忘得差不多了)

概念解读

  • 连通:在无向图中,从任意点 A 都可以到达任意点 B
  • 强连通:在有向图中,从任意点 A 都可以到达任意点 B
  • 弱连通:将有向图看作无向图后,从任意点 A 都可以到达任意点 B

特殊地,单独的点也可以看作强连通分量。

说白了,强连通分量就是环。

而用于求强连通分量的算法,就是大名鼎鼎的 tarjan!

image

而强连通分量最大的应用,则是求出环之后进行缩点。

模板

首先分析模板为什么能用缩点来做

这不显然吗题目就叫缩点

由于每个点的点权只计算一次,所以一个点重复走对于答案的贡献没有影响。

那么,如果走到了环上的一点,则走完环上的所有点一定会是最有答案的一部分。

所以就可以找到所有的强连通分量,然后把强连通分量缩成点,然后构造新图。

而构造出的新图,绝对是一个 DAG(有向无环图)!

然后就可以用拓扑去解决这个 DAG

tarjan

那么,求强连通分量的 tarjan 到底是怎样实现的呢。

众所周知,对树进行 dfs 的时候,有一个名为时间戳的神奇东西,他可以记录 dfs 时的顺序。

而对图进行 dfs 时,则会出现一下几个概念:

  • 树边:属于 dfs 生成树的边。
  • 非树边:不属于生成树的边,分为:
    • 返祖边:在 dfs 时指向祖先节点的边。
    • 前向边:在 dfs 时指向子树中节点的边。
    • 横叉边:在 dfs 时指向已经访问过的点,但不是祖先节点。

而我们求强连通分量,实际上就是找环。

dfs 中,我们用栈来储存目前搜到但没有构成强连通分量的。

tarjan 除了时间戳,还引入了另一个变量:回溯值 low

\(low_u\):在 \(u\) 的子树中能够回溯到的最早的已经在栈中的结点。

显然,环的组成是部分树边和一条返祖边,前向边和横叉边则不会成环。

同样在 dfs 时,只有树边和返祖边会影响 low

low 的初始值均为当前节点的时间戳。

  1. 分析树边:

    当前节点向下发出的树边连接的即为子节点。首先继续 dfs,到回溯时通过与子节点的 low 值取较小值更新当前节点的 low。此时当前节点的 low 满足定义。

  2. 分析回溯边:

    当前节点若发出回溯边,则必定指向自己的祖先,根据 low 的定义,当前节点的 low 即可根据回溯的祖先节点的时间戳更新。

而当分析完所有发出的边后,若时间戳与 low 相等,则说明子树中没有节点能到达更早的在栈中的节点,当前节点即可作为强连通分量的根,将栈中的节点弹出,作为一个强连通分量储存。

代码:

void Tarjan(int now)
{
	in[now]=1;h.push(now);
	dfn[now]=low[now]=++dfns;
	for(int i=fir[now];i;i=nex[i])
	{
		int p=poi[i];
		if(!dfn[p])
		{//没有 dfs 过,属于树边
			Tarjan(p);
			low[now]=min(low[now],low[p]);
		}
		else if(in[p])//dfs 过,但是还在栈中,属于回溯边
			low[now]=min(low[now],dfn[p]);
	}
	if(dfn[now]==low[now])
	{//强连通分量的根,弹出
		int ls;ks++;
		do{
			ls=h.top();h.pop();in[ls]=0;
			bel[ls]=ks;sum[ks]+=a[ls];
		}while(ls!=now);
	}
}

找完强连通分量之后,便要重建图,即缩点。

我们遍历所有的边,若边的两端不在同一强连通分量,则说明重建图中有这个边。

为不与原图冲突,重建图采用 vector 存图(原图采用邻接表存图)。

for(int i=1;i<=m;i++)
{
	int ru=bel[u[i]],rv=bel[v[i]];
	if(ru==rv)continue;
	e[ru].push_back(rv);
	deg[rv]++;
}

由于缩点之后的图绝对是 DAG,所以直接通过拓扑解决后续问题。

for(int i=1;i<=ks;i++)
	if(!deg[i])q.push(i),val[i]=sum[i];
while(q.size())
{
	int now=q.front();q.pop();
	int len=e[now].size();
	for(int i=0;i<len;i++)
	{
		int p=e[now][i];deg[p]--;
		val[p]=max(val[p],val[now]+sum[p]);
		if(deg[p]==0)q.push(p);
	}
}

完整代码

const int inf=1e5+7;
int n,m,ans,a[inf];
int u[inf],v[inf];
int fir[inf],nex[inf],poi[inf],cnt;
void ins(int x,int y)
{
	nex[++cnt]=fir[x];
	poi[cnt]=y;
	fir[x]=cnt;
}
int dfn[inf],low[inf],dfns;
bool in[inf];
stack<int>h;
int ks,bel[inf],sum[inf];
void Tarjan(int now)
{
	in[now]=1;h.push(now);
	dfn[now]=low[now]=++dfns;
	for(int i=fir[now];i;i=nex[i])
	{
		int p=poi[i];
		if(!dfn[p])
		{
			Tarjan(p);
			low[now]=min(low[now],low[p]);
		}
		else if(in[p])
			low[now]=min(low[now],dfn[p]);
	}
	if(dfn[now]==low[now])
	{
		int ls;ks++;
		do{
			ls=h.top();h.pop();in[ls]=0;
			bel[ls]=ks;sum[ks]+=a[ls];
		}while(ls!=now);
	}
}
vector<int>e[inf];
int deg[inf],val[inf];
queue<int>q;
int main()
{
	n=re();m=re();
	for(int i=1;i<=n;i++)
		a[i]=re();
	for(int i=1;i<=m;i++)
	{
		u[i]=re(),v[i]=re();
		ins(u[i],v[i]);
	}
	for(int i=1;i<=n;i++)
		if(!dfn[i])Tarjan(i);
	for(int i=1;i<=m;i++)
	{
		int ru=bel[u[i]],rv=bel[v[i]];
		if(ru==rv)continue;
		e[ru].push_back(rv);
		deg[rv]++;
	}
	for(int i=1;i<=ks;i++)
		if(!deg[i])q.push(i),val[i]=sum[i];
	while(q.size())
	{
		int now=q.front();q.pop();
		int len=e[now].size();
		for(int i=0;i<len;i++)
		{
			int p=e[now][i];deg[p]--;
			val[p]=max(val[p],val[now]+sum[p]);
			if(deg[p]==0)q.push(p);
		}
	}
	for(int i=1;i<=ks;i++)
		ans=max(ans,val[i]);
	wr(ans,'\n');
	return 0;
}

习题

标签:缩点,连通,int,low,inf,now,节点,分量
From: https://www.cnblogs.com/Zvelig1205/p/18314207

相关文章

  • Tarjan(连通性相关) 笔记
    概念点(vertex)、边(edge)无向图中若图中存在两点可以到达,则称这两个点是连通的(connected)若图中任意两点都连通,则称该无向图为连通图(connectedgraph)若图\(G\)中存在一个连通子图\(H\)(\(H\subseteqG\)),没有严格更大的连通子图\(I\)使\(H\varsubsetneqqI\),则称\(H\)......
  • 有向图的强连通分量
    \(\texttt{0x00}\)一些概念什么是“流图”?给定有向图\(G=\{V,E\}\),若存在\(r\inV\),满足从\(r\)出发能到达\(V\)中的所有点,则称\(G\)为一个“流图”,记为\((G,r)\),其中\(r\)称为流图的源点。在一个流图\(G=\{V,E\}\)上从\(r\)出发进行\(\operatorname{df......
  • OpenCV ----像素距离与连通域
    文章目录一.图像像素距离变换1.常用距离的三种定义:(1)欧式距离-----DIST_L2(2)街区距离-----DIST_L1(3)棋盘距离------DIST_C2.distanceTransform()------距离转换函数(1)函数原型(2)运用演示二.图像连通域(1)定义(2)邻域(3)标记连通域函数-----connectedComponents()(3)connecte......
  • 抓间谍(强连通)
    https://www.luogu.com.cn/problem/P1262第5题   抓间谍 查看测评数据信息由于外国间谍的大量渗入,国家安全正处于高度的危机之中。如果A间谍手中掌握着关于B间谍的犯罪证据,则称A可以揭发B。有些间谍收受贿赂,只要给他们一定数量的美元,他们就愿意交出手中掌握的全......
  • 点对(强连通)
    https://www.luogu.com.cn/problem/P4306第2题   点对 查看测评数据信息给定一个N个节点的有向图,统计该有向图的点对个数,如下图:顶点1可达1,2,3,4,5顶点2可达2,3,4,5顶点3可达3,4,5顶点4,5都只能到达自身。所以这张图的点对数为14.给定一张图,请你求出它的点对数对......
  • 遍历(强连通)
    https://www.luogu.com.cn/problem/P2194第3题   遍历 查看测评数据信息给定n个点,m条边的有向图,每个节点有一个权重v(i),你的任务是把n个点遍历一遍,点可以重复经过。允许的操作如下:每次你可以选择一个开始节点i,如果可以从i出发,最后可以回到i节点,那么你的花费为v(i)。问:最......
  • 朋友(强连通)
    https://www.luogu.com.cn/problem/P1407 第1题   朋友 查看测评数据信息我们已知n对男女朋友,称第i对朋友的男方为B(i),女方为G(i)。若某男B(i)与某女G(j)曾经是同学(无论是大学,高中,亦或是幼儿园阶段,i<=j),则当某方与其朋友(即B(i)与G(i)或B(j)与G(j))感情......
  • 连通性相关
    连通性相关强连通分量强连通分量(SCC):极大的强连通子图。Tarjan算法维护一个栈存储搜索到的还未确定强连通分量的点,定义:\(dfn_u\):节点\(u\)被搜索的次序。\(low_u\):\(u\)子树中能回溯到的最小的\(dfn\)。不难得到:一个点子树内的\(dfn\)大于该点的\(dfn\)。......
  • 图论连通性
    【学习笔记】图论连通性啊啊啊啊啊!先引用一篇犇的:)))缩点弱连通:对于有向图中两点\(x\)和\(y\),它们在所有边为无向时存在一个环使它们相连。强连通:对于有向图中两点\(x\)和\(y\),存在一个环使它们相连。强连通子图:对于有向图\(G=(V,E)\),如果对于一个\(V\)的子集......
  • 无向图的连通性(割点与割边)
    割点与割边 割点的求解方法  割点详解 板题:https://www.luogu.com.cn/problem/P3388  第1题   割点 查看测评数据信息给出一个n个点,m条边的无向图,求图的割点。输入格式 第一行输入两个正整数n,m。下面m行每行输入两个正整数x,y表示x到y有一......