强连通分量(学习心得)

定义:有向图强连通分量:在有向图G中,如果两个顶点vi,vj间(vi>vj)有一条从vi到vj的有向路径,同时还有一条从vj到vi的有向路径,则称两个顶点强连通如果有向图G的每两个顶点都强连通,称G是一个强连通图。有向图的极大强连通子图,称为强连通分量。

求强连通分量:

vector<int>pic[maxn];
int dfn[maxn],low[maxn],ans[maxn];
bool ins[maxn];
stack<int>st;
int dind=0,block=0;
int siz[maxn],cud[maxn];
void tarjan(int x)
{
	dind++;
	dfn[x]=low[x]=dind;
	ins[x]=true;
	st.push(x);
	for (int j=0;j<pic[x].size();j++)
	{
		int y=pic[x][j];
		if (dfn[y]==0)
		{
			tarjan(y);
			low[x]=min(low[x],low[y]);
		}
		else if (ins[y]) low[x]=min(low[x],dfn[y]);
	}
	if (low[x]==dfn[x])
	{
		block++;
		siz[block]=0;
		while (true)
		{
			int thi=st.top();
			st.pop();
			ans[thi]=block;
			siz[block]++;
			ins[thi]=false;
			if (thi==x) break;
		}
	}
}

  例题1【UVA-11324 最大团】

分析:先计算强连通分量,然后建一张新图,每个点的权值是它所包含的点的数量,求图的最长链。

代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#include<stack>
using namespace std;
const int maxn=1010;
vector<int> pic[maxn],new_[maxn];
int dfn[maxn],low[maxn],ans[maxn];
bool ins[maxn];
stack<int>st;
int dind=0,block=0;
int siz[maxn],dp[maxn],Ans;
void tarjan(int x)
{
	dind++;
	dfn[x]=low[x]=dind;
	ins[x]=true;
	st.push(x);
	for (int j=0;j<pic[x].size();j++)
	{
		int y=pic[x][j];
		if (!dfn[y]){
			tarjan(y);
			low[x]=min(low[x],low[y]);
		}
		else if (ins[y]) low[x]=min(low[x],dfn[y]);
	}
	if (low[x]==dfn[x])
	{
		block++;
		siz[block]=0;
		while (true)
		{
			int thi=st.top();
			st.pop();
			ans[thi]=block;
			siz[block]++;
			ins[thi]=false;
			if (thi==x) break;
		}
	}
}
int DP(int x){
    if(dp[x]!=-1) return dp[x];
    int Max=0;
    for(int i=0;i<new_[x].size();i++)
        Max=max(Max,DP(new_[x][i]));
    return dp[x]=siz[x]+Max;
}
void init(int n){
	for(int i=1;i<=n;i++) pic[i].clear(),new_[i].clear();
	memset(dfn,0,sizeof(dfn)); memset(low,0,sizeof(low));
	memset(ans,0,sizeof(ans)); memset(ins,0,sizeof(ins));
	while(!st.empty()) st.pop(); dind=block=Ans=0;
	memset(siz,0,sizeof(siz)); memset(dp,-1,sizeof(dp));
}
int main(){
	int n,m,N,u,v;
	scanf("%d",&N);
	while(N--){
		scanf("%d%d",&n,&m);
		init(n);
		while(m--){
			scanf("%d%d",&u,&v);
			pic[u].push_back(v);
		}
		for(int i=1;i<=n;i++)
		if(!dfn[i]) tarjan(i);
		for(int i=1;i<=n;i++)
		for(int j=0;j<pic[i].size();j++)
		if(ans[i]!=ans[pic[i][j]]) new_[ans[pic[i][j]]].push_back(ans[i]);
		for(int i=1;i<=block;i++) Ans=max(Ans,DP(i));
		printf("%d\n",Ans);
	}
	return 0;
}

  例题2【POJ 1236 学校网络】

第一问:缩点后求入度为0的结点数量;

第二问:对于一张有向无环图,可以通过把叶子结点连向根节点使它强连通;

like this:

所以需要连的边数量为max(叶节点,根节点);

叶节点是出度为0的点,根节点是入度为0的点;

注意当只有一个强连通分量时,需要特判,为0;

代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#include<stack>
#include<set>
using namespace std;
const int maxn=110;
vector<int> pic[maxn];
int dfn[maxn],low[maxn],ans[maxn];
bool ins[maxn];
stack<int>st;
set<int>check[maxn];
int dind=0,block=0;
int siz[maxn];
int ans1,in[maxn],out[maxn];

void tarjan(int x)
{
	dind++;
	dfn[x]=low[x]=dind;
	ins[x]=true;
	st.push(x);
	for (int j=0;j<pic[x].size();j++)
	{
		int y=pic[x][j];
		if (!dfn[y]){
			tarjan(y);
			low[x]=min(low[x],low[y]);
		}
		else if (ins[y]) low[x]=min(low[x],dfn[y]);
	}
	if (low[x]==dfn[x])
	{
		block++;
		siz[block]=0;
		while (true)
		{
			int thi=st.top();
			st.pop();
			ans[thi]=block;
			siz[block]++;
			ins[thi]=false;
			if (thi==x) break;
		}
	}
}
int main(){
	int n,i,j;
	scanf("%d",&n);
	for(i=1;i<=n;i++){
		scanf("%d",&j);
		while(j) pic[i].push_back(j),scanf("%d",&j);
	}
	for(int i=1;i<=n;i++)
		if(!dfn[i]) tarjan(i);
	for (i=1;i<=n;i++)
		for (j=0;j<pic[i].size();j++){
			int b1=ans[i],b2=ans[pic[i][j]];
			if (b1==b2) continue;
			if (check[b1].count(b2)) continue;
			check[b1].insert(b2);
			in[b2]++; out[b1]++;
		}
	int root=0,leaf=0;
	for(int i=1;i<=block;i++){
		if(!in[i]) root++;
		if(!out[i]) leaf++;
	}
	printf("%d\n",root);
	if(block==1) printf("0");
	else printf("%d",max(leaf,root));
	return 0;
}

  类似的还有【HDU 2767】

很奇怪的是提交时出现了这样尴尬的问题:

0_0_20950778_21662.cpp
0_0_20950778_21662.cpp(29) : error C3861: “min”:  找不到标识符
0_0_20950778_21662.cpp(31) : error C3861: “min”:  找不到标识符
0_0_20950778_21662.cpp(83) : error C3861: “max”:  找不到标识符

然后在出错的库后面加上了#include "minmax.h",就过了?(莫名其妙)

代码:

#include<iostream>
#include "minmax.h"
#include<cstdio>
#include<cstring>
#include<vector>
#include<stack>
#include<set>
using namespace std;
const int maxn=20010;
vector<int> pic[maxn];
int dfn[maxn],low[maxn],ans[maxn];
bool ins[maxn];
stack<int>st;
set<int>check[maxn];
int dind=0,block=0,siz[maxn];
int in[maxn],out[maxn],T;
void tarjan(int x)
{
	dind++;
	dfn[x]=low[x]=dind;
	ins[x]=true;
	st.push(x);
	for (int j=0;j<pic[x].size();j++)
	{
		int y=pic[x][j];
		if (!dfn[y]){
			tarjan(y);
			low[x]=min(low[x],low[y]);
		}
		else if (ins[y]) low[x]=min(low[x],dfn[y]);
	}
	if (low[x]==dfn[x])
	{
		block++;
		siz[block]=0;
		while (true)
		{
			int thi=st.top();
			st.pop();
			ans[thi]=block;
			siz[block]++;
			ins[thi]=false;
			if (thi==x) break;
		}
	}
}
void init(int n){
	for(int i=1;i<=n;i++)  pic[i].clear(),check[i].clear();;
	memset(dfn,0,sizeof(dfn)); memset(low,0,sizeof(low));
	memset(ans,0,sizeof(ans)); memset(ins,0,sizeof(ins));
	while(!st.empty()) st.pop();
	dind=0;block=0; memset(siz,0,sizeof(siz));
	memset(in,0,sizeof(in)); memset(out,0,sizeof(out));
}
int main(){
	scanf("%d",&T);
	while(T--){
		int n,i,j,m;
		scanf("%d%d",&n,&m);
		init(n);
		for(i=1;i<=m;i++){
			int x,y;
			scanf("%d%d",&x,&y);
			pic[x].push_back(y);
		}
		for(int i=1;i<=n;i++)
			if(!dfn[i]) tarjan(i);
		for (i=1;i<=n;i++)
			for (j=0;j<pic[i].size();j++){
				int b1=ans[i],b2=ans[pic[i][j]];
				if (b1==b2) continue;
				if (check[b1].count(b2)) continue;
				check[b1].insert(b2);
				in[b2]++; out[b1]++;
			}
		int root=0,leaf=0;
		for(int i=1;i<=block;i++){
			if(!in[i]) root++;
			if(!out[i]) leaf++;
		}
		if(block==1) printf("0\n");
		else printf("%d\n",max(leaf,root));
	}
	return 0;
}

  ————————————————————————————————————————————————————————

来自Paper Cloud的博客,未经允许,请勿转载,谢谢

时间: 2024-10-20 07:33:44

强连通分量(学习心得)的相关文章

【学习整理】Tarjan:强连通分量+割点+割边

Tarjan求强连通分量 在一个有向图中,如果某两点间都有互相到达的路径,那么称中两个点强联通,如果任意两点都强联通,那么称这个图为强联通图:一个有向图的极大强联通子图称为强联通分量.   算法可以在 的时间内求出一个图的所有强联通分量. 表示进入结点 的时间 表示从 所能追溯到的栈中点的最早时间 如果某个点 已经在栈中则更新  否则对 进行回溯,并在回溯后更新  #include<iostream> #include<cstdlib> #include<cstdio>

&lt;学习笔记&gt; tarjan 求强连通分量

时间复杂度 O(n+m) dfs 求解. 定义 dfn[n]为n当前的时间戳,low[n]为n最早能追溯到的时间戳,可知 1 for(int i=first[n];i;i=next[i]) 2 { 3 if(!dfn[Rode[i].t]) 4 { 5 Tarjan(Rode[i].t); 6 low[n]=min(low[n],low[Rode[i].t]); 7 } 8 else 9 if(Instack[Rode[i].t]) low[n]=min(low[n],dfn[Rode[i].t

TarJan 算法求解有向连通图强连通分量

[有向图强连通分量] 在有向图G中,如果两个 顶点间至少存在一条路径,称两个顶点强连通(strongly connected).如果有向图G的每两个顶点都强连通,称G是一个强连通图.非强连通图有向图的极大强连通子图,称为强连通分量(strongly connected components). 下图中,子图{1,2,3,4}为一个强连通分量,因为顶点1,2,3,4两两可达.{5},{6}也分别是两个强连通分量. 大体来说有3中算法Kosaraju,Trajan,Gabow这三种!后续文章中将相继

学习心得——day2

今天是学习的第二天,主要讲了图论的选题和DP的优化. 图论 图论主要讲了一些扩展的知识. 如:图上删点 给出一个无向图,每个点都有一个权值. 有一些操作,每次操作删除图上所有权值小于一个数的点. 对于每次操作,询问当前和1号点联通的点有多少个. 思路: 对于这个图而言,图上有n个节点,则图最多有n种形态.我们就可以根据权值的大小对节点进行排序,并且按权值的大小将节点依次加入图中,再用并查集维护出在当前状态下与1号节点相连的点的个数. 在这之后针对每一次删除操作,我们找到在它之前的所有操作中取出一

【转载】有向图强连通分量的Tarjan算法

from byvoid [有向图强连通分量] 在有向图G中,如果两个顶点间至少存在一条路径,称两个顶点强连通(strongly connected).如果有向图G的每两个顶点都强连通,称G是一个强连通图.非强连通图有向图的极大强连通子图,称为强连通分量(strongly connected components). 下图中,子图{1,2,3,4}为一个强连通分量,因为顶点1,2,3,4两两可达.{5},{6}也分别是两个强连通分量. 直接根据定义,用双向遍历取交集的方法求强连通分量,时间复杂度为

有向图强连通分量的Tarjan算法(转)

原文地址:有向图强连通分量的Tarjan算法 [有向图强连通分量] 在有向图G中,如果两个顶点间至少存在一条路径,称两个顶点强连通(strongly connected).如果有向图G的每两个顶点都强连通,称G是一个强连通图.非强连通图有向图的极大强连通子图,称为强连通分量(strongly connected components). 下图中,子图{1,2,3,4}为一个强连通分量,因为顶点1,2,3,4两两可达.{5},{6}也分别是两个强连通分量. 直接根据定义,用双向遍历取交集的方法求强

有向图强连通分量的Tarjan算法——转自BYVoid

[有向图强连通分量] 在有向图G中,如果两个顶点间至少存在一条路径,称两个顶点强连通(strongly connected).如果有向图G的每两个顶点都强连通,称G是一个强连通图.非强连通图有向图的极大强连通子图,称为强连通分量(strongly connected components). 下图中,子图{1,2,3,4}为一个强连通分量,因为顶点1,2,3,4两两可达.{5},{6}也分别是两个强连通分量. 直接根据定义,用双向遍历取交集的方法求强连通分量,时间复杂度为O(N^2+M).更好的

有向图强连通分量的Tarjan算法

有向图强连通分量的Tarjan算法 [有向图强连通分量] 在有向图G中,如果两个顶点间至少存在一条路径,称两个顶点强连通(strongly connected).如果有向图G的每两个顶点都强连通,称G是一个强连通图.非强连通图有向图的极大强连通子图,称为强连通分量(strongly connected components). 下图中,子图{1,2,3,4}为一个强连通分量,因为顶点1,2,3,4两两可达.{5},{6}也分别是两个强连通分量. 直接根据定义,用双向遍历取交集的方法求强连通分量,

ACM 学习心得

ACM 学习心得 STL:完美的艺术品 STL 由四大部分组成:算法.容器.迭代器.仿函数. 算法(algorithm) 算法定义了一组与实现无关的操作,也是 ACM 学习的核心.C++ 算法库的内容全都是一些比较基本的算法,包括移动.转换.遍历.删除.过滤等等.C++ 算法库本身是基于抽象的,在迭代器的抽象下,使得这些算法可以在不同结构的容器中重用.一个比较坑的地方就是我高中的时候学完 C++ 之后报名了 NOIP.那一年刚刚允许用 STL(之前一直不准用),然后我对于标准库的依赖很严重,连快