无向图的Tarjan和有向图求强连通分量的Tarjan很像...注意几个不同...
1、没有栈..所以判断时先是看点有没有访问过...else的时候就直接更新Low...不需要再来个判断instack....
2、DFS时不能从 a 递归到了 b..b又马上从a来更新...所以要多加一个notpre..代表递归b时是从哪个点进去的...防止这种情况..
3、Low相等的点在无向图中就是在一个双连通图中...这个比有向图的方便..有向图还需要用栈来维护..通过判断退栈来判断强连通分量..
备注:第三条是一个环的情况 如果多个环就是错的,做HDU 3394。。。所以用low是否相等来判断是否在一个连通分量中是有问题的
大神的blog http://www.byvoid.com/blog/biconnect/
http://blog.csdn.net/kk303/article/details/6881034
http://poj.org/problem?id=3352
题意:加一些边构造双连通分量,图本来就是连通的
把low相等的缩成一个点,最后形成一棵树 left为 度为1的点 则 (left+1)/2为答案
做的真纠结 RE了一晚上,因为输入的时候漏了while(scanf()) 应为while(~scanf())
贴个代码,睡觉去
代码(边的双连通分量):
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#define Min(a,b)a<b?a:b
#define nMAX 1005
#define mMAX 2010
using namespace std;
int head[nMAX],dfn[nMAX],low[nMAX],out[nMAX],belon[nMAX],stack[nMAX];
int s_edge,times,n,atype,top;
struct Edge
{
int u,v,nxt;
}edge[mMAX];
void addedge(int u,int v)
{
edge[++s_edge].v=v;
edge[s_edge].nxt=head[u];
head[u]=s_edge;
edge[++s_edge].v=u;
edge[s_edge].nxt=head[v];
head[v]=s_edge;
}
void tarjan(int u,int fa)
{
stack[++top]=u;
dfn[u]=low[u]=++times;
for(int e=head[u];e;e=edge[e].nxt)
{
int v=edge[e].v;
if(!dfn[v])
{
tarjan(v,u);
low[u]=Min(low[u],low[v]);
if(dfn[u]<low[v])//边的双连通
{
atype++;
int j;
do
{
j=stack[top--];
belon[j]=atype;
}while(j!=v);
}
}
else if(v!=fa)low[u]=Min(low[u],dfn[v]);
}
}
void init()
{
memset(head,0,sizeof(head));
memset(dfn,0,sizeof(dfn));
memset(out,0,sizeof(out));
times=0;
s_edge=0;
atype=0;
top=0;
}
int main()
{
int CASE=0,i,j,m;
while(~scanf("%d%d",&n,&m))
{
CASE++;
init();
while(m--)
{
scanf("%d%d",&i,&j);
addedge(i,j);
}
tarjan(1,-1);
for(i=1;i<=n;i++)//缩点
{
for(int e=head[i];e;e=edge[e].nxt)
{
int v=edge[e].v;
if(belon[v]!=belon[i])out[belon[i]]++;
}
}
int ans=0;
for(i=0;i<=atype;i++)//从0开始,因为根节点belon=0!!!
if(out[i]==1)ans++;
ans=(ans+1)/2;
printf("%d\n",ans);
}
return 0;
}
下面这个是一样的,只是求边的双连通分量时,有点不同
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#define Min(a,b)a<b?a:b
#define nMAX 1005
#define mMAX 2010
using namespace std;
int head[nMAX],dfn[nMAX],low[nMAX],out[nMAX],belon[nMAX],stack[nMAX];
int s_edge,times,n,atype,top;
struct Edge
{
int u,v,nxt;
}edge[mMAX];
void addedge(int u,int v)
{
edge[++s_edge].v=v;
edge[s_edge].nxt=head[u];
head[u]=s_edge;
edge[++s_edge].v=u;
edge[s_edge].nxt=head[v];
head[v]=s_edge;
}
void tarjan(int u,int fa)
{
stack[++top]=u;
dfn[u]=low[u]=++times;
for(int e=head[u];e;e=edge[e].nxt)
{
int v=edge[e].v;
if(!dfn[v])
{
tarjan(v,u);
low[u]=Min(low[u],low[v]);
}
else if(v!=fa)low[u]=Min(low[u],dfn[v]);
}
if(dfn[u]==low[u])//边的双连通
{
atype++;
int j;
do
{
j=stack[top--];
belon[j]=atype;
}while(j!=u);
}
}
void init()
{
memset(head,0,sizeof(head));
memset(dfn,0,sizeof(dfn));
memset(out,0,sizeof(out));
times=0;
s_edge=0;
atype=0;
top=0;
}
int main()
{
int CASE=0,i,j,m;
while(~scanf("%d%d",&n,&m))
{
CASE++;
init();
while(m--)
{
scanf("%d%d",&i,&j);
addedge(i,j);
}
tarjan(1,-1);
for(i=1;i<=n;i++)//缩点
{
for(int e=head[i];e;e=edge[e].nxt)
{
int v=edge[e].v;
if(belon[v]!=belon[i])out[belon[i]]++;
}
}
int ans=0;
for(i=1;i<=atype;i++)
if(out[i]==1)ans++;
ans=(ans+1)/2;
printf("%d\n",ans);
}
return 0;
}
Poj 3177 构造无向双连通图
http://poj.org/problem?id=3177
直接把poj 3352的代码改了改WA
原来是这个题会有重边
用bool map[5000][5000]来判重,如果map[i][j]==1&&map[j][i]==1则有重边
改进,把map[][]去掉 判重 具体代码
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#define Min(a,b)a<b?a:b
#define nMAX 5005
#define mMAX 20005
using namespace std;
int head[nMAX],dfn[nMAX],low[nMAX],out[nMAX],stack[nMAX],belon[nMAX];
int s_edge,times,n,atype,top;
struct Edge
{
int u,v,nxt;
}edge[mMAX];
void addedge(int u,int v)
{
edge[++s_edge].v=v;
edge[s_edge].nxt=head[u];
head[u]=s_edge;
edge[++s_edge].v=u;
edge[s_edge].nxt=head[v];
head[v]=s_edge;
}
void tarjan(int u,int fa)
{
//fg声明放外面就错,为什么呢??
bool fg=1;
stack[++top]=u;
dfn[u]=low[u]=++times;
for(int e=head[u];e;e=edge[e].nxt)
{
int v=edge[e].v;
if(v==fa&&fg)//判重!!!
{
fg=0;
continue;
}
if(!dfn[v])
{
tarjan(v,u);
low[u]=Min(low[u],low[v]);
if(dfn[u]<low[v])//边的双连通
{
atype++;
int j;
do
{
j=stack[top--];
belon[j]=atype;
}while(j!=v);
}
}
else //if(v!=fa) 有重边的情况,所以去掉这里!!!
low[u]=Min(low[u],dfn[v]);
}
}
void init()
{
memset(head,0,sizeof(head));
memset(dfn,0,sizeof(dfn));
memset(out,0,sizeof(out));
times=0;
s_edge=0;
top=0;
atype=0;
}
int main()
{
int i,j,m;
while(~scanf("%d%d",&n,&m))
{
init();
while(m--)
{
scanf("%d%d",&i,&j);
addedge(i,j);
}
tarjan(1,-1);
for(i=1;i<=n;i++)//缩点
{
for(int e=head[i];e;e=edge[e].nxt)
{
int v=edge[e].v;
if(belon[v]!=belon[i]){out[belon[i]]++;}
}
}
int ans=0;
for(i=0;i<=atype;i++)
if(out[i]==1)ans++;
ans=(ans+1)/2;
printf("%d\n",ans);
}
return 0;
}