题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1269
题目大意:给定一个迷宫,问迷宫中的两个地点能否两两相连,顶点n<=1万,边m<=20万。
解题思路:Tarjan模板题。今天做树形DP的时候遇到双连通缩点,不会做,就来学习下图论的强连通分量和双连通分量,多学些新算法,对开拓思维也有好处。Tarjan算法的核心是通过一个深搜来实现,简单地说就是把后面遇到的连通的点往前面出现的点收缩,最后同一个强连通分量的所有点会收缩到一个点。如果设个low来标记他们属于哪个强连通分量,初始时他们low[i] = index最后同一个强连通分量的点的low[i]都会一样。详细的参见Here。
Tarjan算法十分优美。 本题是用邻接表建图,操作起来略显麻烦,如果用vector会更加简洁。
测试数据:
1 0
4 3
1 2
2 3
3 1
3 3
1 2
2 3
3 1
3 4
1 2
2 1
2 3
3 2
3 3
1 2
2 3
3 2
代码:
#include <stdio.h>
#include <string.h>
#define MIN 10010
#define MAX 100011
#define min(a,b) (a)<(b)?(a):(b)
struct node {
int v;
node *next;
}*head[MAX],tree[MAX];
int num,index;
int n,m,top,st[MIN];
int isstack[MIN],vis[MIN];
int ptr,dfn[MIN],low[MIN];
void Initial() {
top = 1,num = 0;
index = ptr = 1;
memset(dfn,0,sizeof(dfn));
memset(head,NULL,sizeof(head));
memset(isstack,0,sizeof(isstack));
}
void AddEdge(int x,int y) {
tree[ptr].v = y;
tree[ptr].next = head[x],head[x] = &tree[ptr++];
}
void Tarjan(int i){
int j,v = -1;
node *p = head[i]; //当前节点的邻接顶点
st[++top] = i; //将当前节点入栈
isstack[i] = 1; //标记已经入栈,栈内的点都是连通的,强连通待判断
dfn[i] = low[i] = index++; //这里记录出现的次序
while (p != NULL) { //遍历邻接顶点
if (!dfn[p->v]) { //如果顶点p->v未出现过
Tarjan(p->v); //往下搜索
low[i] = min(low[i],low[p->v]); //收缩到最小的那个点,此时p->v顶点的low已计算
}
else if (isstack[p->v])
low[i] = min(low[i],dfn[p->v]);//收缩到最小的那个点,因为还在栈内low还未计算
p = p->next;
}
if (dfn[i] == low[i]) { //将属于i的那个强连通分量都出栈,都是i的人了..
do {
v = st[top--];
isstack[v] = 0;
}while (v != i);
num++;
}
if (num > 1) return;
}
int main()
{
int i,j,k,a,b;
while (scanf("%d%d",&n,&m),n+m) {
Initial();
for (i = 1; i <= m; ++i) {
scanf("%d%d",&a,&b);
AddEdge(a,b);
}
for (i = 1; i <= n && num < 2; ++i)
if (!dfn[i]) Tarjan(i);
if (num == 1) printf("Yes\n");
else printf("No\n");
}
}
本文ZeroClock原创,但可以转载,因为我们是兄弟。