tarjan算法求强连通分量

hiho详解链接:http://hihocoder.com/contest/hiho54/problem/1


强连通分量的定义:对于有向图上的2个点a,b,若存在一条从a到b的路径,也存在一条从b到a的路径,那么称a,b是强连通的。对于有向图上的一个子图,若子图内任意点对(a,b)都是满足强连通,则称该子图为强连通子图。非强连通图有向图的极大强连通子图,称为强连通分量。特别的,任意一个和其他点都不相连的单个点也是一个强连通分量。


需要特别注意的是,强连通一般是对于有向图而言的。


tarjan求强连通分量的模板:

void tarjan(int u)
{
	dfs_pos[u] = low[u] = ++jilu;
	sta.push(u);//每个节点进入时先压栈
	vis[u] = -1;//vis = -1表示已经在栈中了,0表示从未被访问过,1表示该点已经被标记成一个强连通分量了
	int v;
	for(int i = head[u];~i;i = e[i].nex)
	{
		v = e[i].v;
		if(vis[v] == 0){//对于从未被访问过的点,继续tarjan深搜
			tarjan(v);
			low[u] = min(low[v],low[u]);
		}else if(vis[v] == -1)//对于已经访问过的点,若不是直接相连的父亲而是祖先则更新
			low[u] = min(low[v],low[u]);
	}
	if(dfs_pos[u] == low[u])//dfs序和最近父亲相等,将栈中一直到u的点都弹出并标记为同一个强连通分量
	{
		while(!sta.empty())
		{
			int temp = sta.top();
			mark[temp] = u;
			sta.pop();
			if(temp == u)break;
		}
		vis[u] = 1;
	}
}




hiho的原题题解:

数据中会有重边,这样的话在我原来的拓扑找最大的时候会有问题。。。很坑。。。后来改成dfs求就对了

#include <bits/stdc++.h>
#define mem(a,b) memset(a,b,sizeof(a))
typedef long long ll;
using namespace std;
const int maxn = 250005;
struct ppp
{
	int v,nex;
}e[maxn],ee[maxn];
int head[maxn / 10],tole,n,m,vis[maxn / 10],jilu;
int w[maxn / 10];
int dfs_pos[maxn / 10],low[maxn / 10];
stack<int> sta;
int mark[maxn / 10];
void make_edge(int u,int v)
{
	e[tole].v = v;e[tole].nex = head[u];head[u] = tole++;
}
void tarjan(int u)
{
	dfs_pos[u] = low[u] = ++jilu;
	sta.push(u);//每个节点进入时先压栈
	vis[u] = -1;//vis = -1表示已经在栈中了,0表示从未被访问过,1表示该点已经被标记成一个强连通分量了
	int v;
	for(int i = head[u];~i;i = e[i].nex)
	{
		v = e[i].v;
		if(vis[v] == 0){//对于从未被访问过的点,继续tarjan深搜
			tarjan(v);
			low[u] = min(low[v],low[u]);
		}else if(vis[v] == -1)//对于已经访问过的点,若不是直接相连的父亲而是祖先则更新
			low[u] = min(low[v],low[u]);
	}
	if(dfs_pos[u] == low[u])//dfs序和最近父亲相等,将栈中一直到u的点都弹出并标记为同一个强连通分量
	{
		while(!sta.empty())
		{
			int temp = sta.top();
			mark[temp] = u;
			sta.pop();
			if(temp == u)break;
		}
		vis[u] = 1;
	}
}
int in[maxn / 10];
vector<int> vec[maxn / 10];
int maxx;
void dfss(int u,int fa,int haha)
{
	int v;
	maxx = max(haha,maxx);
	for(int i = 0;i < vec[u].size();i++)
	{
		v = vec[u][i];
		if(v == fa)continue;
		dfss(v,u,haha + w[v]);
	}
}
int main()
{
	while(~scanf("%d%d",&n,&m))
	{
		int a,b;
		mem(head,-1);tole = 0;jilu = 0;
		mem(vis,0);
		while(!sta.empty())sta.pop();
		for(int i = 1;i <= n;i++)cin>>w[i];
		for(int i = 0;i < m;i++)
		{
			scanf("%d%d",&a,&b);
			make_edge(a,b);
		}
		mark[1] = 1;
   		for(int i = 1;i <= n;i++)
			if(!vis[i])
				tarjan(i);
		for(int i = 1;i <= n;i++)
			if(mark[i] != i)
				w[mark[i]] += w[i];
		for(int i = 1;i <= n;i++)vec[i].clear(),in[i] = 0;
		for(int i = 1;i <= n;i++)
			for(int j = head[i];~j;j = e[j].nex)
				if(mark[i] != mark[e[j].v])
				{
					vec[mark[i]].push_back(mark[e[j].v]);
					in[e[j].v]++;
				}
		maxx = w[1];
		dfss(1,-1,w[1]);
		cout<<maxx<<endl;
	}
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值