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

题目链接:P5318 【深基18.例3】查找文献

补:输入格式

第一行两个整数 (n, m)

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

(其实这题的 (n) 并没有什么卵用……)

补:输出格式

(2) 行,每行(n) 个整数,表示遍历的顺序。

审题&解法

这题就是给一张图,让你求对它进行深度优先搜索(DFS)和广度优先搜索的(BFS)遍历的顺序。当然是用邻接表来存图。

这题的难点(我认为的难点)在于这:

如果有很多篇文章可以参阅,请先看编号较小的那篇

我们必须先看编号小的,否则输出结果就不能正确。

尽管题目的样例是排好了序的,但是题目描述中并没有保证输入的数据一定是有序的,而且因为邻接表最后加的边会最先访问,最早加的边会最后访问,我们需要以head[]数组为第一关键字从小到大排序,以to[]数组为第二关键字从大到小排序。

使用结构体和sort(),再借助cmp()就可以完成排序。

排序的cmp()函数如下:

bool cmp(node a, node b) {
  if (a.from == b.from) return a.to > b.to;
  return a.from < b.from;
}

如果没看懂就好好理解一下。

代码

#include <cstdio>
#include <iostream>
#include <queue>
#include <algorithm>
#include <cstring>
using namespace std;
struct node {
  int from, to, nxt;
}a[1000006];

int head[100005], d[100005];
bool vis[100005];
int tot;

bool cmp(node a, node b) {
  if (a.from == b.from) return a.to > b.to; //如果两个的from[]相等,就按to从大到小排序
  return a.from < b.from;                   //否则按from从小到大排序
}

inline void add(int x, int y) {
  a[++tot].to = y;
  a[tot].nxt = head[x], head[x] = tot;
}

inline int read() {
  int X = 0; bool flag = 1; char ch = getchar();
  while (ch < '0' || ch > '9') {if (ch == '-') flag = 0; ch = getchar();}
  while (ch >= '0' && ch <= '9') {X = (X << 1) + (X << 3) + ch - '0'; ch = getchar();}
  if (flag) return X;
  return ~ (X - 1);
}

inline void write(int X) {
  if (X < 0) {putchar('-'); X = ~ (X - 1);}
  int s[50], top = 0;
  while (X) {s[++top] = X % 10; X /= 10;}
  if (!top) s[++top] = 0;
  while (top) putchar(s[top--] + '0');
  putchar(' ');
  return;
}

void dfs(int x) {
  write(x);
  vis[x] = true;   //标记到过
  for (int i = head[x]; i; i = a[i].nxt) { //循环出边
    if (!vis[a[i].to])  //如果没到过
      dfs(a[i].to);     //就搜索它
  }
  return;
}

void bfs() {      //广搜
  queue<int> que; //先定义一个队列
  que.push(1); d[1] = 1;            //将1加入队列
  while (!que.empty()) {            //循环,直到队列为空
    int x = que.front(); que.pop(); //从队头取出一个值,把它弹出
    write(x);                       //把它输出
    for (int i = head[x]; i; i = a[i].nxt) { //循环他的每一条出边,如果没有访问过,就把它加入队列
      int y = a[i].to;
      if (d[y]) continue;
      d[a[i].to] = 1;               //标记它被访问过(本来存深度,但在这题它退化了),本来应该为d[y] = d[x] + 1;
      que.push(y);                  //将加入队列
    }
  }
}

signed main() {
  int m;
  m = read(); m = read();
  for (int i = 1; i <= m; i++) {          //先在结构体里输入,但不加边
    a[i].from = read(); a[i].to = read(); 
  }
  sort(a + 1, a + m + 1, cmp);            //输入完之后排序
  for (int i = 1; i <= m; i++) {          //排完序之后加边,
    add(a[i].from, a[i].to);              //这样可以保证先搜索到的一定是编号最小的
  }
  dfs(1);                                 //从1开始深搜
  putchar('
');                          //输出完深搜的就换行
  bfs();                                  //广搜
  putchar('
');
  return 0;
}
原文地址:https://www.cnblogs.com/g-mph/p/14636448.html