您的位置:首页 > 其它

Tarjan算法学习1-双连通

2013-04-09 23:57 260 查看
太久不做题,发现很多学过的算法又通通不记得了,或许因为当时既没有深刻理解,也没有及时总结。最近开始复习图论,说是复习,不如说重新学习更加恰当。算法是进入大学紧跟着c语言接触到的第二项“技术”,虽然在竞赛中就好像最终创业失败一样不得善终,总还是希望不辜负那段年轻的时光,也不辜负难得一份坚持了这么久的兴趣。

我的图论学习也不是从Tarjan算法开始的,而是最近在看双连通分量相关问题的时候又想起了当年学习Tarjan时的一些好笑经历。当时学习完求强连通分量的Tarjan解法,又发现双连通分量也是用这个算法,以当时对其浅薄的认识还以为是同一个算法却不知为何可以解决不同的问题。后来才知道包括解决LCA问题在内的这些算法,都是由图灵奖获得者Robert Tarjan这位计算机泰斗所创,可惜他没有创造出更多更艺术的算法名字(==)。为了能够保留我再次学习算法的这些浅薄认识,也为了表达我对Tarjan的膜拜,我决定从Tarjan算法开始我的学习总结。

双连通分量

点连通度:直观来说,就是使一个连通图成为非连通图所需要去掉的最小点数。
边连通度:同理就是使一个连通图成为非连通图所需要去掉的最小边数。
割点:去掉这个点及所连的边,原图不连通
割边(桥):去掉这条边原图不连通
双连通图:一个无向连通图的点连通度大于1(不存在割点),那么该图就是点双连通图,如果边双连通度大于1,该图就是边双连通图(不存在桥,或者说图的任意两点间都有两条不相交的路径,如果有相交的边,显然这条边就是桥)。
双连通分量:是一个图的极大双连通子图。
双连通还是有很大的实际意义的,这一点我觉得也是图论有意思且牛叉的地方所在。比如一个通信网络,某一个通信点故障(割点),或一条通信线路故障(桥)会不会影响整个网络的通信。
在一个图中,显然是由割点将各个双连通分量连接起来的(要么它们根本不连通),要求双连通分量,关键就是要把割点找到。
什么样的点是割点呢?有下面两个图(对于一个无向图进行一次dfs后的搜索树):

View Code

#include<cstdio>
#include<cstring>
#include<vector>
#include<algorithm>
using namespace std;
const int N = 1005;

vector<int>mp
;
vector<int>cut
;
int G

,low
,dfn
,stack
,mark
,color
;
int m,n,cnt,stack_ptr,cut_num,flag;
void DFS(int x, int father)
{
low[x] = dfn[x] = ++cnt;
stack[stack_ptr++] = x;
int len = mp[x].size();
for (int i = 0; i < len; i++){
int t = mp[x][i];
if (!dfn[t]){
DFS(t,x);
low[x] = min(low[x], low[t]);
if (dfn[x] <= low[t]) {
cut[cut_num].push_back(x);
while (stack[stack_ptr-1] != t)
cut[cut_num].push_back(stack[--stack_ptr]);
cut[cut_num++].push_back(stack[--stack_ptr]);
}
}
else if (t != father)
low[x] = min(low[x], dfn[t]);
}
}
void check(int x, int d, int col,int fa)
{
color[x] = col;
int len = mp[x].size();
for (int i = 0; i < len; i++){
int t = mp[x][i];
if (t == fa) continue;
vector<int>::iterator it = find(cut[d].begin(),cut[d].end(),t);
if (it != cut[d].end())
if (color[t] == 0)
check(t,d,-col,x);
else if (color[t] == col)
flag = 1;
}
}
int Tarjan()
{
memset(low,0,sizeof(low));
memset(dfn,0,sizeof(dfn));
memset(cut,0,sizeof(cut));
cnt = stack_ptr = cut_num = 0;
for (int i = 1; i <= n; i++)
if (!dfn[i])
DFS(i,-1);
memset(mark,0,sizeof(mark));
for (int i = 0; i < cut_num; i++){
memset(color,0,sizeof(color));
int len = cut[i].size();
flag = 0;
if(len >= 3){
check(cut[i][0],i,1,-1);
if (flag)
for (int j = 0; j < len; j++)
mark[cut[i][j]] = 1;
}
}
int ans = 0;
for (int i = 1; i <= n; i++)
if (!mark[i])
ans++;
return ans;
}
int main()
{
while (scanf("%d%d",&n,&m) && n+m)
{
int a,b;
for (int i = 0; i <= n; i++){
mp[i].clear();
cut[i].clear();
}
memset(G,0,sizeof(G));
while (m--){
scanf("%d%d",&a,&b);
G[a][b] = G[b][a] = 1;
}
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++)
if (i!=j && G[i][j]==0)
mp[i].push_back(j);
printf("%d\n",Tarjan());
}

return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: