P5318 【深基18.例3】查找文献 题解

题目链接 https://www.luogu.com.cn/problem/P5318

【深基18.例3】查找文献

题目描述

小K 喜欢翻看洛谷博客获取知识。每篇文章可能会有若干个(也有可能没有)参考文献的链接指向别的博客文章。小K 求知欲旺盛,如果他看了某篇文章,那么他一定会去看这篇文章的参考文献(如果他之前已经看过这篇参考文献的话就不用再看它了)。

假设洛谷博客里面一共有 \(n(n\le10^5)\) 篇文章(编号为 1 到 \(n\))以及 \(m(m\le10^6)\) 条参考文献引用关系。目前小 K 已经打开了编号为 1 的一篇文章,请帮助小 K 设计一种方法,使小 K 可以不重复、不遗漏的看完所有他能看到的文章。

这边是已经整理好的参考文献关系图,其中,文献 X → Y 表示文章 X 有参考文献 Y。不保证编号为 1 的文章没有被其他文章引用。

请对这个图分别进行 dfs 和 bfs,并输出遍历结果。如果有很多篇文章可以参阅,请先看编号较小的那篇(因此你可能需要先排序)。

输入格式

\(m+1\) 行,第 1 行为 2 个数,\(n\)\(m\),分别表示一共有 \(n(n\le10^5)\) 篇文章(编号为 1 到 \(n\))以及\(m(m\le10^6)\) 条参考文献引用关系。

接下来 \(m\) 行,每行有两个整数 \(X,Y\) 表示文章 X 有参考文献 Y。

输出格式

共 2 行。
第一行为 dfs 遍历结果,第二行为 bfs 遍历结果。

样例 #1

样例输入 #1

8 9
1 2
1 3
1 4
2 5
2 6
3 7
4 7
4 8
7 8

样例输出 #1

1 2 5 6 3 7 8 4 
1 2 3 4 5 6 7 8

思路描述

我们可以把每个洛谷博客看作一个结点,而将每篇参考文献的关系看为一条边,所以这道题就是一道图的遍历的题目。
我们需要对此图进行\(dfs\)遍历和\(bfs\)遍历,所以我们用邻接表来存储图,那为什么不用邻接矩阵来存储呢?因为邻接矩阵的空间复杂度为\(O(n^2)\),邻接表的空间复杂度为\(O(m+n)\),因为题目中的\(n\le10^5\),用邻接矩阵是会爆的,所以设一个vector用邻接表存储图。
因为在\(dfs\)时,需要从一个结点回跳到另一个结点,所以我们会用到递归,在\(bfs\)时,需要用一种特定的数据结构来存储顺序,用队列(queue)是最合适的。
在遍历时,为了防止重复遍历,就用一个bool类型的vis数组进行标记,注意两次遍历用同一数组时要在中间将其清空一遍。
因为我们我们要先看较小的那篇,所以需要将vector进行排序。

\(dfs\)时,读文献的顺序是这样的:

1.看博客1的参考文献2

2.看文献2的参考文献5

3.文献5没有参考文献,所以看文献2的参考文献6

4.文献2的参考文献看完了,继续看博客1的参考文献3

5.看文献3的参考文献7

6.看文献7的参考文献8

7.文献3已经看完了,看博客1的参考文献4

8.文献7已经被看过了,就不再看了

9.文献8已经被看过了

所以\(DFS\)部分代码为:

void dfs(int x) {
	vis[x] = true;//标记以避免重复访问
	cout<<x<<' ';//输出
	int len=vec[x].size();//存储vec的长度
	for(int i=0; i<len; i++)
		if(!vis[vec[x][i]]) { //如果没有被遍历过
			vis[vec[x][i]]=true;//则将其标记
			dfs(vec[x][i]);//继续遍历
		}
}

\(bfs\)时,读文献的顺序是这样的:

1.看博客1的参考文献2

2.看博客1的参考文献3

3.看博客1的参考文献4

4.看完博客1的参考文献,看文献2的参考文献5

5.看文献2的参考文献6

6.看完文献2的参考文献,看文献3的参考文献7

7.文献4的参考文献7已经被看过了

8.看文献4的参考文献8

9.文献7的参考文献8已经被看过了,就不再看了

所以\(BFS\)部分代码为:

void bfs(int x) {
	memset(vis, false, sizeof(vis));//清空标记
	vis[x] = true;//标记以避免重复访问
	q.push(x);//入队
	while(!q.empty()) { //如果队列不是空的,进入循环
		int x=q.front();//将x设为队首
		q.pop();//弹出队首的那个点,不然会一直在第一层遍历
		cout<<x<<' ';
		int len=vec[x].size();//存储vec的长度
		for(int i=0; i<len; i++)
			if(!vis[vec[x][i]]) { //如果没有被遍历过
				vis[vec[x][i]]=true;//则将其标记
				q.push(vec[x][i]);//入队
			}
	}
}

主函数部分:

int main() {
	cin>>n>>m;//输入
	for(int i=1; i<=m; i++) {
		int x,y;
		cin>>x>>y;
		vec[x].push_back(y);//邻接表存储
	}
	for (int i=1; i<=n; i++)
		sort(vec[i].begin(), vec[i].end());//vector排序
	dfs(1);
	cout<<endl;
	bfs(1);
	return 0;
}

头文件部分:

#include<iostream>
#include<cstdio>
#include<cmath>
#include<vector>
#include<stack>
#include<queue>
#include<algorithm>
#include <cstring>
using namespace std;
const int MAXN=100005;
int n,m;
vector <int> vec[MAXN];
queue <int> q;
bool vis[MAXN];
//不必过多解释

完整代码:

#include<iostream>
#include<cstdio>
#include<cmath>
#include<vector>
#include<stack>
#include<queue>
#include<algorithm>
#include <cstring>
using namespace std;
const int MAXN=100005;
int n,m;
vector <int> vec[MAXN];
queue <int> q;
bool vis[MAXN];
void dfs(int x) {
	vis[x] = true;
	cout<<x<<' ';
	int len=vec[x].size();
	for(int i=0; i<len; i++) {
		if(!vis[vec[x][i]]) {
			vis[vec[x][i]]=true;
			dfs(vec[x][i]);
		}
	}
}
void bfs(int x) {
	memset(vis, false, sizeof(vis));
	vis[x] = true;
	q.push(x);
	while(!q.empty()) {
		int x=q.front();
		q.pop();
		cout<<x<<' ';
		int len=vec[x].size();
		for(int i=0; i<len; i++) {
			if(!vis[vec[x][i]]) {
				vis[vec[x][i]]=true;
				q.push(vec[x][i]);
			}
		}
	}
}
int main() {
	cin>>n>>m;
	for(int i=1; i<=m; i++) {
		int x,y;
		cin>>x>>y;
		vec[x].push_back(y);
	}
	for (int i=1; i<=n; i++)
		sort(vec[i].begin(), vec[i].end());
	dfs(1);
	cout<<endl;
	bfs(1);
	return 0;
}

END:如有不足,欢迎指出

2024/8/27.修改了文章细节

posted @ 2023-07-18 19:13  zzrlmt  阅读(486)  评论(9)    收藏  举报