黔西南布依族苗族自治州网站建设_网站建设公司_安全防护_seo优化
2025/12/29 22:31:10 网站建设 项目流程

【题目描述】

当我们阅读文章时,每篇文章可能会有若干个(也有可能没有)参考文献的链接指向别的文章。如果小Q他看了某篇文章,那么他一定会去看这篇文章的参考文献(如果他之前已经看过这篇参考文献的话就不用再看它了)。

假设图书室里面一共有 n(n≤105) 篇文章(编号为 1 到 n)以及 m(m≤106) 条参考文献引用关系。目前小 Q 已经开始阅读编号为 1 的一篇文章,请帮助小Q设计一种方法,使小Q可以不重复、不遗漏的看完所有他能看到的文章。

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

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

【输入】

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

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

【输出】

共 2 行。

第一行为 DFS 遍历结果,第二行为 BFS 遍历结果。

【输入样例】

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

【输出样例】

1 2 5 6 3 7 8 4 1 2 3 4 5 6 7 8
1. 解题思路

这道题的核心难点不在于 DFS 或 BFS 的原理,而在于如何控制遍历顺序

题目通常要求:当一个点有多个邻居时,必须优先访问编号较小的那个(即字典序最小)。

  • 如果用vector存图:非常简单,把每个点的邻居vector拿出来sort一遍就行。

  • 如果用链式前向星:这里有要注意的

    • 链式前向星是“头插法”。

    • 这意味着:后加入的边,会排在链表的最前面。(如果有很多篇文章可以参阅,请先看编号较小的那篇(因此你可能需要先排序))题目中告诉我们需要排序

    • 举例:如果按顺序加入边1->21->3,链表里实际的存储顺序是1->3->2。遍历时会先走 3,再走 2。导致答案错误。

解决方案:既然链式前向星会把顺序“倒过来”,那我们在存图之前,先把数据倒着排。

  • 排序规则

    1. 起点u从小到大(这就不用说了,为了按顺序处理点)。

    2. 终点v从大到小(这样倒着插进去,输出出来就是从小到大了)。

  • 效果:

    比如有边 (1, 2) 和 (1, 5)。排序后让 (1, 5) 排在 (1, 2) 前面。

    • 先存(1, 5)-> 表头是 5。

    • 再存(1, 2)-> 2 插到 5 前面 -> 表头是 2 -> 链表变成2 -> 5

    • 遍历时:先访问 2,再访问 5。完美符合题目要求。


2. 完整代码
#include <iostream> #include <cstring> #include <algorithm> #include <queue> using namespace std; int h[100010]; int vtex[1000010]; int nxt[1000010]; int idx; int vis1[1000010];//dfs标记数组 int vis2[1000010];//bfs标记数组 queue<int>q; struct gen{ int u; int v; }a[1000005]; //先按照起点从小到大排序,再按照终点从大到小排序(因为链式前向星是倒着存进去的,所以我们要从大到小排序终点) bool cmp(gen c,gen d){ if(c.u!=d.u) return c.u<d.u; else return c.v>d.v; } void bfs(int x){ q.push(x); while(!q.empty()){ int tmp=q.front(); cout<<tmp<<" "; q.pop(); int p=h[tmp]; while(p!=-1){//不是空指针 int tmp=vtex[p]; if(vis2[tmp]==0){//说明该点没有入队过 vis2[tmp]=1;//打上标记 q.push(tmp);//入队 } p=nxt[p];//指针指向当前指针所指向数的下一个数 } } } void dfs(int x){ cout<<x<<" "; int p=h[x]; while(p!=-1){//如果指针不指向空 if(vis1[vtex[p]]==0){//如果指针指向的点没有走过 vis1[vtex[p]]=1;//打上标记 dfs(vtex[p]);//把指针指向的点作为下一个起点继续走 } p=nxt[p];//找p指针指向点的下一个点 } } void addedge(int x,int y){ vtex[idx]=y; nxt[idx]=h[x]; h[x]=idx++; } int main(){ ios::sync_with_stdio(false); cin.tie(0); int n,m; cin>>n>>m; //先把关系存进a数组 for(int i=1;i<=m;i++){ int x,y; cin>>x>>y; a[i].u=x; a[i].v=y; } //如果有很多篇文章可以参阅,先看编号较小的那篇(所以需要先a数组排序 sort(a+1,a+m+1,cmp); memset(h,-1,sizeof(h));//初始化h数组 //邻接表存图 for(int i=1;i<=m;i++) addedge(a[i].u,a[i].v); vis1[1]=1;//把起点打上标记 dfs(1); cout<<endl; vis2[1]=1;//把起点打上标记 bfs(1); return 0; }
3. 总结
  • 存图顺序:在使用链式前向星时,如果题目对遍历顺序有要求(如字典序),必须先对边排序

  • 排序方式起点升序,终点降序

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询