图论---强连通分量与缩点---Tarjan算法

强连通分量

在有向图G中,如果每一个点对 (ui,vi),都既存在从 ui 到 vi 的路径,也存在从 vi 到 ui 的路径,则图G成为强连通图,强连通分量定义为有向图中的极大强连通子图
容易证明,强连通分量是对有向图点集的一种划分。

强连通分量的求法

对于一个有向图G,想求出G有多少强连通分量,以及每个强连通分量包含哪些顶点,一般我们采用Tarjan算法。(实际还有一种叫Kosaraju的算法,但因为它的时间复杂度常数大,因此没有Tarjan优秀)。

两个重要属性

由于执行Tarjan算法的需要,我们赋予有向图G的每个结点两个属性,用数组表示出来,一个叫 dfn[ ],一个叫 low[ ]。含义如下:
dfn[i]:表示顶点 i 在深度优先搜索树中,被搜索到的次序。它有一个专业术语叫 “时间戳”
low[i]:表示顶点 i 通过自身或其子节点能回溯到的最小的时间戳。
算法思想:
算法基于深度优先搜索,首先每访问一个新节点,初始化 dfn[i] = low[i] = 访问次序,并把该结点保存在一个栈内。在回溯的过程中更新low[ ] 的值。使得 low[i] 为顶点 i 通过后继能回到的最早的时间戳。
如果更新后的 low[i] 还是等于 dfn[i],则说明:在搜索树中,以顶点 i 为根节点的子树中的顶点属于一个强连通分量。此时需要将栈中 i 顶点以及 i 顶点上方的所有顶点出栈并染色。

代码

void Tarjan(int u)
{
	dfn[u] = ++deep;
	low[u] = deep;
	stack[++top] = u;
	vis[u] = 1;
	int sz = G[u].size();
	for(int i=0;i<sz;i++)
	{
		int v = G[u][i];
		if(dfn[v]==0)
		{
			Tarjan(v);
			low[u] = min(low[u], low[v]);
		}
		else
		{
			if(vis[v]==1)
			    low[u] = min(low[u], low[v]);
		}
	}
	if(dfn[u]==low[u])
	{
		color[u] = ++sum;
		Color[sum].push_back(u);
		vis[u] = 0;
		while(stack[top]!=u)
		{
			color[stack[top]] = sum;
			Color[sum].push_back(stack[top]);
			vis[stack[top]] = 1;
			top--;
		}
		top--;
	}
}

由于原图不一定连通,所以要枚举所有结点,如果dfn[i] = 0 则执行Tarjan。
每个结点访问一次,因此这个算法的时间复杂度是线性的。

缩点

由强连通分量的性质:“任意两个结点可以相互到达。” 可知,如果能到达强连通分量的任何一个顶点,就可以到达该强连通分量的任何结点。在研究某些问题(尤其DP居多),习惯上就直接把强连通分量视为一个顶点。我们叫 “缩点” 操作,其本质是根据Tarjan求强连通分量后得到的染色信息重新构图。
易知经过缩点后得到的图为DAG

//获得重构图,a[],和b[]表示一条边连接的两个顶点
//我的方法是我自己的习惯,应该比较麻烦,对不同的题目可能有更好的处理方法
void Get_TG()
{
	for(int i=1;i<=r;i++)
    {
    	if(color[a[i]]!=color[b[i]])
    	{
    		int sz = TG[color[a[i]]].size(), ok = 0;
    		for(int j=0;j<sz;j++)
    		{
    			if(TG[color[a[i]]][j]==color[b[i]])
    			{
    				ok = 1;
    				break;
				}
			}
			if(!ok)
			{
				TG[color[a[i]]].push_back(color[b[i]]);
				rdu[color[b[i]]]++;
			}
		}
	}
}

例题

缩点+简单DP:洛谷P3387 【模板】缩点.

缩点:洛谷P2341 [USACO03FALL][HAOI2006]受欢迎的牛 G.

缩点:洛谷P1262 间谍网络.

缩点+简单DP:洛谷P3627 [APIO2009]抢掠计划.

缩点+DAG最长链计数:洛谷P2272 [ZJOI2007]最大半连通子图.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值