无向图的路径判断
题目描述:
现有一个由1,2,3,4...,n个点,m条边组成的无向图。请判断给定的t组数据x, y(x表示起点,y表示终点),针对每组数据是否存在一条通路,如果存在则输出Yes,否则输出No
输入:
第1行:n m t (n、m、t使用空格隔开,其中n表示结点的数量,结点由1开始编号;m表示边的数量;t表示待判断的起点、终点有多少对。2<=n、m、t<=1000)
接下来的m行,每行一对整数a b,a、b使用空格隔开,表示结点a与结点b之间存在一条边
接下来的t行,每行一对整数x y, x、y使用空格隔开,如果结点x与结点y之间存在一条通路则输出Yes,否则输出No;每行一个。
输出:
如果指定的起点与终点之间存在一条通路则输出Yes,否则输出No,每行一个
样例输入:
5 3 4
1 2
3 4
1 4
2 3
2 4
3 5
2 5
样例输出:
Yes
Yes
No
No
思路:
这道题十分的朴素,简单,基本上是一个模板题,但是,这次我会介绍三个做法,其中最后一个做法是拓展,可以做了解哈
1 - [BFS 做法]
这个十分简单啊,就是将每个点都bfs一遍,它连通块(经过的点)上的所有点都记为1,最后询问的时候,看他们之间是否记为1,是一的话就说明这两个是联通着的,输出“Yes"即可。
代码:
#include <bits/stdc++.h>
using namespace std;
vector<int> edge[1005];
int n,m,t;
queue <int> pancheng;
int ans[1005][1005];
int vis[1005];
void bfs(int wb){
memset(vis,0,sizeof(vis));
pancheng.push(wb);
vis[wb] = 1;
while(pancheng.size()){
int tmp = pancheng.front();
pancheng.pop();
ans[wb][tmp] = 1;
ans[tmp][wb] = 1;
for(int i = 0;i < edge[tmp].size();i++){
int to = edge[tmp][i];
if(vis[to] == 0){
vis[to] = 1;
pancheng.push(to);
}
}
}
}
int main(){
scanf("%d%d%d",&n,&m,&t);
for(int i = 1;i <= m;i++){
int u,v;
scanf("%d%d",&u,&v);
edge[u].push_back(v);
edge[v].push_back(u);
}
for(int i = 1;i <= n;i++){
bfs(i);
}
for(int i = 1;i <= t;i++){
int x,y;
scanf("%d%d",&x,&y);
if(ans[x][y] == 1){
cout <<"Yes"<<endl;
}
else{
cout<<"No"<<endl;
}
}
return 0;
}
2 - [DFS 做法]
这个做法也是十分的简单,先存图,询问的时候,dfs x 点,在dfs的时候,遍历周围每一个点,再将每一个点dfs一次,如果再dfs的路径上遇到了 y 点,可以直接全部停止,return true
代码:
#include <bits/stdc++.h>
using namespace std;
vector<int> edge[1005];
int n,m,t;
int vis[1005];
bool dfs(int wb,int mb){
vis[wb] = 1;
if(wb == mb){
return true;
}
else{
int len = edge[wb].size();
for(int i = 0;i < len;i++){
int to = edge[wb][i];
if(vis[to] == 0){
vis[to] = 1;
if(dfs(edge[wb][i],mb)){
return true;
}
vis[to] = 0;
}
}
}
return false;
}
int main(){
scanf("%d%d%d",&n,&m,&t);
for(int i = 1;i <= m;i++){
int u,v;
scanf("%d%d",&u,&v);
edge[u].push_back(v);
edge[v].push_back(u);
}
for(int i = 1;i <= t;i++){
int x,y;
scanf("%d%d",&x,&y);
memset(vis,0,sizeof(vis));
if(dfs(x,y)){
cout<<"Yes"<<endl;
}
else{
cout<<"No"<<endl;
}
}
return 0;
}
3 - [并查集 做法]
这个做法只做简单的拓展,代码看起来简单易懂,且速度快,很好写。
但是其中的原理,需要细细消化。
并查集,可以简单的理解成,可以走通的所有点,都聚集在一个集合里(这个集合是树的形式),要判断这两个点是否联通,只用判断它们是不是在一棵树里,即它们是不是同一个树根。
其实并查集的道理也就那样,就是上面讲述的一样。但是有一种特殊的情况,假如,这棵树运气特别不好,这棵树恰好连成一条链,那么查找的话就要将这棵树全部遍历一遍,时间复杂度还是很大。此处我们就可以用一种方法——————路径压缩
路径压缩的原理也是很简单,就是将其父亲节点的根变成这个点的父节点即可。
其实并查集就这些了,下面给出完整代码
代码:
#include <bits/stdc++.h>
using namespace std;
int n,m,t;
int fa[1005];//存每个节点的父节点,根的父节点就是它自己(并查集一般都是这么写的)
int find_root(int x){//找这个点根
if(fa[x] == x){
return x;
}
else{
int ret = find_root(fa[x]);
fa[x] = ret;
return ret;
}
}
void merge(int x,int y){//合并两个点,将其所在的两个树的根连在一起
int rx = find_root(x);
int ry = find_root(y);
if(rx != ry){
fa[rx] = ry;
}
}
int main(){
scanf("%d%d%d",&n,&m,&t);
for (int i = 1;i <= n;i++){
fa[i] = i;
}//一开始所有点的父节点都是它自己(根的父节点就是它自己)
for (int i = 1;i <= m;i++){
int a,b;
scanf("%d%d",&a,&b);
merge(a,b);//两个点之间有联通,将其所在的两个树相连
}
for (int i = 1;i <= t;i++){
int a,b;
scanf("%d%d",&a,&b);
if(find_root(a) == find_root(b)){//如果是同一个根(即同一棵树)
cout<<"Yes"<<endl;
}
else{
cout<<"No"<<endl;
}
}
return 0;
}
本文来自博客园,作者:XDFZ武斌,转载请注明原文链接:https://www.cnblogs.com/XDFZwb1523/p/16154436.html

浙公网安备 33010602011771号