一:树的重心(DFS)

  • 邻接表用vector存储的效率不如使用数组存储效率高。
  • 输入的数据超过100万的时候需要考虑使用scanf
  • 树的BFS和DFS因为每一个点都是只遍历一次,所以时间复杂度是O(M+ N)

下面的写的博客非常好
https://www.acwing.com/solution/content/13513/
代码剩下的注释不写了,直接看上面的人的就很好

  1. #include <iostream>
  2. #include <cstring>
  3. #include <algorithm>
  4. using namespace std;
  5. const int N = 1e5 + 10, M = 2 * N; // 无向图,用有向图模拟,树是无环的无向连通图,边的数目是2n - 2
  6. int h[N], e[M], ne[M], idx; // h代表邻接表的n个顶点 e数组存储的是相邻的点是什么,下标用idx去表示,idx的作用就是邻接表数据结构
  7. int ans = N; // ans最终的答案。
  8. int n;
  9. bool st[N]; // 标记每个点有没有被访问
  10. void add(int a, int b)
  11. {
  12. e[idx] = b, ne[idx] = h[a], h[a] = idx ++; // 链表的标准操作,这里建立邻接表只需要多一个下标参数
  13. }
  14. int dfs(int u)
  15. {
  16. int sum = 1;
  17. int res = 0;
  18. st[u] = true;
  19. for (int i = h[u]; i != -1; i = ne[i])
  20. {
  21. int j = e[i];
  22. if (!st[j])
  23. {
  24. int s = dfs(j);
  25. sum += s;
  26. res = max(res, s);
  27. }
  28. }
  29. res = max(res, n - sum); // 这里也没有什么问题,详细考虑过
  30. ans = min(ans, res);
  31. return sum;
  32. }
  33. int main()
  34. {
  35. memset(h, -1, sizeof h);
  36. cin >> n;
  37. for (int i = 0; i < n - 1; i++)
  38. {
  39. int a, b;
  40. cin >> a >> b;
  41. add(a, b), add(b, a);
  42. }
  43. dfs(1);
  44. cout << ans << endl;
  45. return 0;
  46. }
  • 二刷问题很多,关键就是思路不清晰,每一轮的递归最起码应该知道求解的是什么,这个题二刷,甚至搞错返回值是什么
  • 逻辑,点需要记录有没有访问,已经访问在哪里写都搞错
  • 无向图
  • 还有就是最小值,初始值设置最大
  • 这个题无向图的好处就是,遍历的起点无所谓,从哪个起点开始,因为是无向图,都可以看作根节点。如果你不用无向图,很有可起始dfs(1)不对,因为可能不是根节点了,你用了无向图,就一定是双边,因为用的邻接表写的,两个点一定相互连接,所以一定要有状态,表示某个点已经访问,避免再次访问。

    二:图中点的层次

    广搜很轻松就能解决,图的添加,广搜的公式。
    image.pngimage.png ```cpp

    include

    include

    include

using namespace std;

const int N = 1e5 + 10, M = 2 * N; int h[N], e[M], ne[M], idx; int n, m; int d[N], q[N];

void add(int a, int b) { e[idx] = b, ne[idx] = h[a], h[a] = idx ++; }

int bfs() { q[0] = 1; memset(d, -1, sizeof d); d[1] = 0; int hh = 0, rr = 0; while (hh <= rr) { int u = q[hh++]; for (int i = h[u]; i != -1; i = ne[i]) { int j = e[i]; if (d[j] == -1) { d[j] = d[u] + 1; q[++rr] = j; } } } return d[n]; }

int main() { memset(h, -1, sizeof d); cin >> n >> m; for (int i = 0; i < m; i++) { int a, b; cin >> a >> b; add(a, b); } cout << bfs() << endl; return 0; }

  1. <a name="dj2j3"></a>
  2. ## 三:拓扑序列
  3. 有向无环图一定存在拓扑序列
  4. ```cpp
  5. #include <iostream>
  6. #include <cstring>
  7. #include <algorithm>
  8. using namespace std;
  9. const int N = 1e5 + 10;
  10. int h[N], e[N], ne[N], idx;
  11. int n, m;
  12. int d[N], q[N];
  13. void add(int a, int b)
  14. {
  15. e[idx] = b, ne[idx] = h[a], h[a] = idx++;
  16. }
  17. int topsort()
  18. {
  19. int hh = 0, rr = -1;
  20. for (int i = 1; i <= n; i++) //找到所有的入读为0的点,题目条件是从1-n,所以这里遍历的时候,从1开始
  21. {
  22. if (d[i] == 0)
  23. {
  24. q[++rr] = i;
  25. }
  26. }
  27. while (hh <= rr)
  28. {
  29. int u = q[hh++];
  30. for (int i = h[u]; i != -1; i = ne[i])
  31. {
  32. int j = e[i];
  33. d[j]--; // 因为出队列的时候, 相当于一条边被删除了,自然指向的点的入度减一
  34. if (d[j] == 0)
  35. {
  36. q[++rr] = j;
  37. }
  38. }
  39. }
  40. return rr;
  41. }
  42. int main()
  43. {
  44. memset(h, -1, sizeof h); // 构建图不用多说
  45. cin >> n >> m;
  46. for (int i = 0; i < m; i++)
  47. {
  48. int a, b;
  49. cin >> a >> b;
  50. add(a, b);
  51. d[b] += 1;
  52. }
  53. if (topsort() == n - 1) // 形成拓扑序列的时候,队列中存储了0-n-1的所有元素
  54. {
  55. for (int i = 0; i < n; i++)
  56. {
  57. cout << q[i] << " ";
  58. }
  59. cout << endl;
  60. } else {
  61. cout << "-1" << endl;
  62. }
  63. return 0;
  64. }