引发这个思考前,我们先引入一个题目:
题目:误落迷宫
题目描述
杰洛特在和希里分兵追赶狂猎杂鱼部队时,不慎落入迷宫,但这样怎么瞒得过杰洛特得智慧呢?
杰洛特通过使用猎魔人出色的感知能力获取了每一个通道点两端得连通性,并且了解到自己得初始点,如果相链接通道产生环路则是一个失败的探索(会让自己原地兜圈圈)
给定一个数量n表示联通边数
接着给定n组联通边和一个初始位置
请判断杰洛特所走的路线是否会产生环路输入
多组数据输入
第一行输入一个n
接下来n行每行两个数字表示相互连通的点
再接下来输入一个p表示初始位置
n < 100输出
Yes表示可以离开迷宫No表示产生原地兜圈圈无法离开迷宫
样例输入
4 1 2 2 3 3 1 3 4 1 4 1 2 2 3 3 1 3 4 4
样例输出
No Yes
简单来说,这道题的目的是判断一个点是否在回环上,
注意,并不是判断图是否有环,Case1和Case2均是有环图,只是1正是在环上,而4不在环上,所以Case1会迷路,而Case2不会迷路。
思考一:关于图的回环,我们最容易想到的就是并查集思想,也就是我们常说的找爹数组,在经典的最小生成树算法Kruskal中它被广泛应用(关于这个知识点可看:畅通工程-Kruskal-最小生成树),一条边的两个端点找爹找爹呗,找到它们的祖先,若是同一个祖先,那么这条边肯定构成回环了。但是这道题笔者认为并不适用,因为我们得知道某个点是否在回环上。
思考二:我们或许会有另一个想法,我们遍历一下这个图不就好了,以所求点为起点,深搜这个图,如果跑一圈又回来了,那么这个点在回环上。理想很丰满,现实很骨感,在深搜中,我们走这个点即会标记其为访问过状态,则不会再访问它了,山无棱天地和,都会与君绝。
思路三:如下图所示,我们遍历+回溯这个图,但我们标记访问的是【边】,点是可以重复访问的,如果我们走到一个点,它的连接点的边已经访问过了,说明走了一圈环路又回到了它 ,那么说明我们走进了回环。
实现方式如下:
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn = 100 + 10;
int vis[maxn][maxn]; //记录边的访问 ,这是关键
int map[maxn][maxn]; //无向图,map[i][j]==1代表ij之间有边
int n,m,flag;
void DFS(int t){
for(int i=1;i<=n+1;i++){
if(map[t][i]==1 && vis[t][i]==1){ //走到一个点,它的连接点的边已经访问过了,说明走了一圈环路又回到了它
flag=0;
return ;
}
if(map[t][i]==1 && vis[t][i]==0){
vis[t][i]=1;
DFS(i);
vis[t][i]=0;
}
}
}
int main(){
while(cin>>n){
memset(vis,0,sizeof(vis));
memset(map,0,sizeof(map));
flag=1;
int a,b;
for(int i=0;i<n;i++){
cin>>a>>b;
map[a][b]=1;
}
cin>>m;
DFS(m);
printf("%s\n",flag?"Yes":"No");
}
}