tarjan算法(二)

刚刚又写了一遍UVA 315,之前看着别人的代码写过一次,大概就是这周四才写的,刚刚写发现判断条件完全忘记了···难受,原来我忘东西忘的这么快,之后要找个时间把之前的题再复习一下啊,可是就剩三个星期就要省赛了,心态炸裂·····

这篇是用tarjan算法来找割点

割点:若删掉某点后,原连通图分裂为多个子图,则称该点为割点

对于一个连通图,如果v不经过u就无法达到u的祖先节点,那么u就是一个割点,因为删去u之后,v就与u的祖先节点分开了,一个连通图变成了两个子图(对于有向图我们先不讨论,我现在甚至都不明白有向图到底有没有割点的概念,而且有向图的连通性判断也和无向图不太一样,啊,好想再上一年的离散数学课啊),根据这个定义,我们转化成tarjan算法中表示的话就是,low[v]>=dfn[u](这里为什么不是low[v]>=low[u],很显而易见,low[u]可能更新之后变成了包含u的搜索树的最小的根节点的数值,而v可能有条路到u的祖先,但是还不足以到达low[u],这种情况会把u误判为割点),low[v]>=dfn[u]代表,v没有一条路可以到达u的祖先,很明显,如果有这样的路,low[v]就会更新为dfn[u的祖先]。

注意这里如果v是根节点,那么由于它没有父亲节点,所以需要特判,如果以根节点为根的搜索树大于1的话,那么根节点就是一个割点。

感觉写的好乱啊,而且其中有很多东西真的不太容易用语言表达,画个图看看能理解的更清楚,下面贴UVA 315

Description

Download as PDF

A Telephone Line Company (TLC) is establishing a new telephone cable network. They are connecting several places numbered by integers from 1 to N. No two places have the same number. The lines are bidirectional and always connect together two places and in each place the lines end in a telephone exchange. There is one telephone exchange in each place. From each place it is possible to reach through lines every other place, however it need not be a direct connection, it can go through several exchanges. From time to time the power supply fails at a place and then the exchange does not operate. The officials from TLC realized that in such a case it can happen that besides the fact that the place with the failure is unreachable, this can also cause that some other places cannot connect to each other. In such a case we will say the place (where the failure occured) is critical. Now the officials are trying to write a program for finding the number of all such critical places. Help them.

Input

The input file consists of several blocks of lines. Each block describes one network. In the first line of each block there is the number of places N < 100. Each of the next at most N lines contains the number of a place followed by the numbers of some places to which there is a direct line from this place. These at most N lines completely describe the network, i.e., each direct connection of two places in the network is contained at least in one row. All numbers in one line are separated by one space. Each block ends with a line containing just 0. The last block has only one line with N = 0.

Output

The output contains for each block except the last in the input file one line containing the number of critical places.

Sample Input

5
5 1 2 3 4
0
6
2 1 3
5 4 6 2
0
0

Sample Output

1
2


题意:给你一些连通块,让你找出有几个割点

代码:

#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
#define Maxn 105
int head[Maxn];
int u[Maxn*Maxn];
int v[Maxn*Maxn];
int _next[Maxn*Maxn];

int dfn[Maxn];
int low[Maxn];
int re[Maxn];

int ans;

void dfs(int k,int num,int root,int fa){
	int cnt=0;
	dfn[k]=low[k]=num;
	for(int i=head[k];i!=-1;i=_next[i]){
		int e=v[i];
		if(!dfn[e]) {
			cnt++;
			dfs(e,++num,root,k);
			low[k]=min(low[e],low[k]);
			if(k!=root &&dfn[k]<=low[e]) re[k]=1; //这里必须确保<u,v>是树枝边,否则low[e]可能更新过,导致误判(和为什么不能是low[k]<=low[e]相似) 
			else if(k==root&&cnt>1) re[k]=1;//对于根节点要特判,如果根节点有多个子树,那么是割点 
		}
		else low[k]=min(low[k],dfn[e]);
		
	}
}

int main(){
	int n;
	while(scanf("%d",&n)&&n){
		memset(head,-1,sizeof(head));
		memset(dfn,0,sizeof(dfn));
		memset(re,0,sizeof(re));
		ans=0;
		int s,t;
		int c=0;
		while(scanf("%d",&s)&&s){
			while(getchar()!='\n'){
				scanf("%d",&t);
				u[c]=s;
				v[c]=t;
				_next[c]=head[s];
				head[s]=c++;
				u[c]=t;
				v[c]=s;
				_next[c]=head[t];
				head[t]=c++;
			}
		} 
		for(int i=1;i<=n;i++){
			if(!dfn[i]) dfs(i,1,i,-1);
		}
		for(int i=1;i<=n;i++) if(re[i]) ans++;
		printf("%d\n",ans);
	}
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Tarjan算法和Kosaraju算法都是求解有向图强连通分量的算法,它们的时间复杂度都为O(N+M),其N为图节点数,M为图边数。 Tarjan算法的基本思想是通过DFS遍历图的节点,并在遍历的过程维护一个栈,用于存储已经遍历过的节点。在遍历的过程,对于每个节点,记录它被遍历到的时间戳和能够到达的最小时间戳,当一个节点的最小时间戳等于它自身的时间戳时,说明这个节点及其之前遍历到的节点构成了一个强连通分量,将这些节点从栈弹出即可。 Kosaraju算法的基本思想是先对原图进行一次DFS,得到一个反向图,然后再对反向图进行DFS。在第次DFS的过程,每次从未被访问过的节点开始遍历,遍历到的所有节点构成一个强连通分量。 两种算法的具体实现可以参考以下代码: ```python # Tarjan算法 def tarjan(u): dfn[u] = low[u] = timestamp timestamp += 1 stk.append(u) for v in graph[u]: if not dfn[v]: tarjan(v) low[u] = min(low[u], low[v]) elif v in stk: low[u] = min(low[u], dfn[v]) if dfn[u] == low[u]: scc = [] while True: v = stk.pop() scc.append(v) if v == u: break scc_list.append(scc) # Kosaraju算法 def dfs1(u): vis[u] = True for v in graph[u]: if not vis[v]: dfs1(v) stk.append(u) def dfs2(u): vis[u] = True scc.append(u) for v in reverse_graph[u]: if not vis[v]: dfs2(v) # 构建图和反向图 graph = [[] for _ in range(n)] reverse_graph = [[] for _ in range(n)] for u, v in edges: graph[u].append(v) reverse_graph[v].append(u) # Tarjan算法求解强连通分量 dfn = [0] * n low = [0] * n timestamp = 1 stk = [] scc_list = [] for i in range(n): if not dfn[i]: tarjan(i) # Kosaraju算法求解强连通分量 vis = [False] * n stk = [] scc_list = [] for i in range(n): if not vis[i]: dfs1(i) vis = [False] * n while stk: u = stk.pop() if not vis[u]: scc = [] dfs2(u) scc_list.append(scc) ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值