hdu1269强连通模板 (一)

2014-11-24 02:59:08 · 作者: · 浏览: 4

该题也可以用双向dfs求强连通分量,这里给出Tarjan算法的模板,更适合变形。

也贴上双向dfs的代码吧。

代码Tarjan(这是网上有注释的代码,讲得很细):

[cpp]
#include
#include
#include
#include
using namespace std;

#define MAXN 10010
#define MAXM 100010

struct Edge
{
int v, next;
}edge[MAXM]; //边结点数组

int first[MAXN], stack[MAXN], DFN[MAXN], Low[MAXN], Belong[MAXM];
// first[]头结点数组,stack[]为栈,DFN[]为深搜次序数组,Belong[]为每个结点所对应的强连通分量标号数组
// Low[u]为u结点或者u的子树结点所能追溯到的最早栈中结点的次序号
int instack[10010]; // instack[]为是否在栈中的标记数组
int n, m, cnt, scnt, top, tot;

void init()
{
cnt = 0;
scnt = top = tot = 0; //初始化连通分量标号,次序计数器,栈顶指针为0
memset(first, -1, sizeof(first));
memset(DFN, 0, sizeof(DFN)); //结点搜索的次序编号数组为0,同时可以当是否访问的数组使用
}

void read_graph(int u, int v) //构建邻接表
{
edge[tot].v = v;
edge[tot].next = first[u];
first[u] = tot++;
}

void Tarjan(int v) //Tarjan算法求有向图的强连通分量
{
int min, t;
DFN[v] = Low[v] = ++tot; //cnt为时间戳
instack[v] = 1; //标记在栈中
stack[top++] = v; //入栈
for(int e = first[v]; e != -1; e = edge[e].next)
{ //枚举v的每一条边
int j = edge[e].v; //v所邻接的边
if(!DFN[j])
{ //未被访问
Tarjan(j); //继续向下找
if(Low[v] > Low[j]) Low[v] = Low[j]; // 更新结点v所能到达的最小次数层
}
else if(instack[j] && DFN[j] < Low[v])
{ //如果j结点在栈内,
Low[v] = DFN[j];
}
}
if(DFN[v] == Low[v])
{ //如果节点v是强连通分量的根
scnt++; //连通分量标号加1
do
{
t = stack[--top]; //退栈
instack[t] = 0; //标记不在栈中
Belong[t] = scnt; //出栈结点t属于cnt标号的强连通分量
}while(t != v); //直到将v从栈中退出
}
}

void solve()
{
for(int i = 1; i <= n; i++) //枚举每个结点,搜索连通分量
if(!DFN[i]) //未被访问
Tarjan(i); //则找i结点的连通分量
}

int main()
{
while(scanf("%d%d",&n,&m) && (n || m))
{
init();
while(m--)
{
int u, v;
scanf("%d%d", &u, &v);
read_graph(u, v);
}
solve(); //求强连通分量
if(scnt == 1) printf("Yes\n"); //只有一个强连通分量,说明此图各个结点都可达
else printf("No\n");
}
return 0;
}

#include
#include
#include
#include
using namespace std;

#define MAXN 10010
#define MAXM 100010

struct Edge
{
int v, next;
}edge[MAXM]; //边结点数组

int first[MAXN], stack[MAXN], DFN[MAXN], Low[MAXN], Belong[MAXM];
// first[]头结点数组,stack[]为栈,DFN[]为深搜次序数组,Belong[]为每个结点所对应的强连通分量标号数组
// Low[u]为u结点或者u的子树结点所能追溯到的最早栈中结点的次序号
int instack[10010]; // instack[]为是否在栈中的标记数组
int n, m, cnt, scnt, top, tot;

void init()
{
cnt = 0;
scnt = top = tot = 0; //初始化连通分量标号,次序计数器,栈顶指针为0
memset(first, -1, sizeof(first));
memset(DFN, 0, sizeof(DFN)); //结点搜索的次序编号数组为0,同时可以当是否访问的数组使用
}

void read_graph(int u, int v) //构建邻接表
{
edge[tot].v = v;
edge[tot].next = first[u];
first[u] = tot++;
}

void Tarjan(int v) //Tarjan算法求有向图的强连通分量
{
int min, t;
DFN[v] = Low[v] = ++tot; //cnt为时间戳
instack[v] = 1; //标记在栈中
stack[top++] = v; //入栈
for(int e = first[v]; e != -1; e = edge[e].next)
{ //枚举v的每一条边
int j = edge[e].v; //v所邻接的边
if(!DFN[j])
{ //未被访问
Tarjan(j); //继续向下找
if(Low[v] > Low[j]) Low[v] = Low[j]; // 更新结点v所能到达的最小次