题目链接: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
代码:
#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原创,但可以转载,因为我们是兄弟。