【HDU】 1269 迷宫城堡 强连通分量

8 篇文章 0 订阅
5 篇文章 0 订阅

题目传送门:HDU1269

今天,突发奇想,对于强连通分量产生了浓厚的兴趣,于是就想学一学。

当然,对于一个初学的蒟蒻来说,Tarjan算法是比较简单易懂的(因为我打的就是Tarjan),我选的题目也是比较水。

在Tarjan算法里,最难懂的就是一个节点所在子树的最早搜索次序了,这些我都会详细讲解。

搜索次序还是比较好懂的,如果会搜索的同学应该都懂什么是搜索次序吧。

现在我们假设我们已经搜到节点x,当前搜索树的大小为size,一个节点所在子树的最早搜索次序为t,搜索次序为d。

节点x的搜索次序为size+1(这一点应该没问题吧)。

我们遍历所有节点x可以到达的节点y:

1.若节点y没有被搜索过,则搜索节点y,t[x]=min(t[x],t[y])

2.若节点y被搜索过且节点y所在的强连通分量未被确定,t[x]=min(t[x],d[y])

若遍历完所有节点y后,t[x]=d[x]说明以u为根的搜索子树上所有节点是一个强连通分量。

内个……你问我怎么证明?我也不知道啊……但是画图之后,好像是举不出反例的吧。就把这个当成一个结论来用吧。

可以用一个栈来求每一个强连通分量中有哪些元素,具体的,看代码吧……


对于我的代码,可能需要一些注释:ans表示这张图中有几个强连通分量,size表示当前的搜索树的大小,sy数组表示该节点属于哪一个强连通分量。

b数组的作用:对于节点x,若b[x]=0,说明该节点未被搜索过或该节点所在的强连通分量已确定;若b[x]=1,说明该节点已被搜索过,且该节点所在的强连通分量未确定。

有人可能会问:你的b[x]=0有两个含义,搜索时不会重复搜索吗?

请这些同学好好阅读一下下面的代码,并画图辅助,你会发现:对于节点x,当该节点被搜索完毕时,d[x]!=0,b[x]=0。再次搜索到这个节点时,好像会被略过……

就这样吧……(为自己的代码的简洁点个赞)

附上AC代码:

#include <cstdio>
#include <cstring>
#include <vector>
#include <stack>
using namespace std;

stack <int> s;
vector <int> map[10010],scc[10010];
int n,m,t[10010],d[10010],b[10010],size,ans,x,y,sy[10010];

void intt(void){
	memset(t,0,sizeof t),memset(d,0,sizeof d),memset(b,0,sizeof b),size=ans=0;
	for (int i=1; i<=n; ++i) map[i].clear(),scc[i].clear();
	while (!s.empty()) s.pop();
}

void so(int x){
	b[x]=1,t[x]=d[x]=++size,s.push(x);
	for (int i=0; i<map[x].size(); ++i)
		if (!d[map[x][i]]) so(map[x][i]),t[x]=min(t[x],t[map[x][i]]);
			else if (b[x]) t[x]=min(t[x],d[map[x][i]]);
	if (d[x]==t[x]){
		++ans;
		while (!s.empty()){
			int p=s.top();s.pop(),b[p]=0,sy[p]=ans,scc[ans].push_back(p);
			if (p==x) break;
		}
	}
	return;
}

int main(void){
	while (scanf("%d%d",&n,&m)&&n+m){
		intt();
		for (int i=1; i<=m; ++i) scanf("%d%d",&x,&y),map[x].push_back(y);
		for (int i=1; i<=n; ++i) if (!d[i]) so(i);
		if (ans>1) puts("No"); else puts("Yes");
	}
	return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值