• PAT A1013 Battle Over Cities分数 25(连通块数量,割点是针对连通块的,不是整个图)


    1013 Battle Over Cities

    分数 25

    作者 CHEN, Yue

    单位 浙江大学

    It is vitally important to have all the cities connected by highways in a war. If a city is occupied by the enemy, all the highways from/toward that city are closed. We must know immediately if we need to repair any other highways to keep the rest of the cities connected. Given the map of cities which have all the remaining highways marked, you are supposed to tell the number of highways need to be repaired, quickly.

    For example, if we have 3 cities and 2 highways connecting city1​-city2​ and city1​-city3​. Then if city1​ is occupied by the enemy, we must have 1 highway repaired, that is the highway city2​-city3​.

    Input Specification:

    Each input file contains one test case. Each case starts with a line containing 3 numbers N (<1000), M and K, which are the total number of cities, the number of remaining highways, and the number of cities to be checked, respectively. Then M lines follow, each describes a highway by 2 integers, which are the numbers of the cities the highway connects. The cities are numbered from 1 to N. Finally there is a line containing K numbers, which represent the cities we concern.

    Output Specification:

    For each of the K cities, output in a line the number of highways need to be repaired if that city is lost.

    Sample Input:

    1. 3 2 3
    2. 1 2
    3. 1 3
    4. 1 2 3

    Sample Output:

    1. 1
    2. 0
    3. 0


     * 求图的割点,割边只是针对连通块而言,并不是针对整个图。
     *
     * 对于此题,去掉某个点,进行图的遍历,求图的连通块数量,要整个图连通,则至少需要
     * 连通块数量减一 条边。
     *
     * 图的遍历用邻接表存储,需要O(M)的时间复杂度。共检测k个点,进行k遍图的遍历,
     * 总的时间复杂度为:O(M*K);



    求图的割点,割边(啊哈算法)_疯疯癫癫才自由的博客-CSDN博客
     

    1)用图的割点算法加求连通块数量求解,有一个测试点通不过:

    1. /**
    2. * 求图的割点,割边只是针对连通块而言,并不是针对整个图。
    3. *
    4. * 对于此题,去掉某个点,进行图的遍历,求图的连通块数量,要整个图连通,则至少需要
    5. * 连通块数量减一条边。
    6. *
    7. * 图的遍历用邻接表存储,需要O(M)的时间复杂度。共检测k个点,进行k遍图的遍历,
    8. * 总的时间复杂度为:O(M*K);
    9. */
    10. #include <iostream>
    11. #include <algorithm>
    12. #include <vector>
    13. using namespace std;
    14. const int N = 1010;
    15. vector<int> Adj[N];
    16. int num[N], low[N];
    17. int flag[N];
    18. bool hs[N];
    19. int idx, root;
    20. int Nv,Ne,k;
    21. void dfs(int u, int fath)
    22. {
    23. int child = 0;
    24. ++idx;
    25. num[u] = idx;
    26. low[u] = idx;
    27. for(int i=0; i<Adj[u].size(); ++i)
    28. {
    29. int v = Adj[u][i];
    30. if(num[v] == 0)
    31. {
    32. dfs(v, u);
    33. low[u] = min(low[u], low[v]);
    34. if(u != root && low[v] >= num[u])
    35. flag[u] = 1;
    36. ++child;
    37. if(u == root && child >= 2)
    38. flag[u] = 1;
    39. }
    40. else if(v != fath)
    41. low[u] = min(low[u], num[v]);
    42. }
    43. }
    44. void traver()
    45. {
    46. for(int i=1; i<=Nv; ++i)
    47. if(num[i] == 0)
    48. {
    49. root = i;
    50. dfs(i, root);
    51. }
    52. }
    53. void DFS(int u, int rm)
    54. {
    55. hs[u] = 1;
    56. for(int i=0; i<Adj[u].size(); ++i)
    57. {
    58. int v = Adj[u][i];
    59. if(hs[v] == 0)
    60. DFS(v, rm);
    61. }
    62. }
    63. int FolldFill(int u)
    64. {
    65. fill(hs,hs+N,0);
    66. hs[u] = 1;
    67. int cnt = 0;
    68. for(int i = 1; i<=Nv; ++i)
    69. {
    70. if(hs[i] == 0)
    71. {
    72. DFS(i, u);
    73. ++cnt;
    74. }
    75. }
    76. return cnt;
    77. }
    78. int main()
    79. {
    80. cin >> Nv >> Ne >> k;
    81. int u, v;
    82. while (Ne -- )
    83. {
    84. cin >> u >> v;
    85. Adj[u].push_back(v);
    86. Adj[v].push_back(u);
    87. }
    88. traver();
    89. while(k--)
    90. {
    91. cin >> u;
    92. if(flag[u] == 0) //题目并不保证整个地图是只有一个连通块,所以此处并不能用
    93. puts("0"); //割点算法来判断
    94. else
    95. cout << FolldFill(u)-1 << endl;
    96. }
    97. return 0;
    98. }

      * 求图的割点,割边只是针对连通块而言,并不是针对整个图。
     *
     * 对于此题,去掉某个点,进行图的遍历,求图的连通块数量,要整个图连通,则至少需要
     * 连通块数量减一条边。
     *
     * 图的遍历用邻接表存储,需要O(M)的时间复杂度。共检测k个点,进行k遍图的遍历,
     * 总的时间复杂度为:O(M*K);

    2)AC代码,连通块数量求解:

    1. /**
    2. * 求图的割点,割边只是针对连通块而言,并不是针对整个图。
    3. *
    4. * 对于此题,去掉某个点,进行图的遍历,求图的连通块数量,要整个图连通,则至少需要
    5. * 连通块数量减一条边。
    6. *
    7. * 图的遍历用邻接表存储,需要O(M)的时间复杂度。共检测k个点,进行k遍图的遍历,
    8. * 总的时间复杂度为:O(M*K);
    9. */
    10. #include <iostream>
    11. #include <algorithm>
    12. #include <vector>
    13. using namespace std;
    14. const int N = 1010;
    15. vector<int> Adj[N];
    16. bool hs[N];
    17. int Nv,Ne,k;
    18. void DFS(int u, int rm)
    19. {
    20. hs[u] = 1;
    21. for(int i=0; i<Adj[u].size(); ++i)
    22. {
    23. int v = Adj[u][i];
    24. if(hs[v] == 0)
    25. DFS(v, rm);
    26. }
    27. }
    28. int FolldFill(int u)
    29. {
    30. fill(hs,hs+N,0);
    31. hs[u] = 1; //先把顶点u去掉
    32. int cnt = 0;
    33. for(int i = 1; i<=Nv; ++i)
    34. {
    35. if(hs[i] == 0)
    36. {
    37. DFS(i, u);
    38. ++cnt;
    39. }
    40. }
    41. return cnt;
    42. }
    43. int main()
    44. {
    45. cin >> Nv >> Ne >> k;
    46. int u, v;
    47. while (Ne -- )
    48. {
    49. cin >> u >> v;
    50. Adj[u].push_back(v);
    51. Adj[v].push_back(u);
    52. }
    53. while(k--)
    54. {
    55. cin >> u;
    56. cout << FolldFill(u)-1 << endl;
    57. }
    58. return 0;
    59. }

    3)割点 + 连通块求解

     * 倒是也可以用割点算法来求解,但是割点是针对连通图而言,所以需要是对整个图的
     * 所有连通块都进行割点求解,然后再求解出整个图的连通块个数,并且把每个连通块
     * 的顶点个数求解出来,假定此时求出的连通块个数为cnt,
     * 1)如果有孤立的点存在,敌人把此点占用,那么连通块个数减一,所以需要连的边
     * 数为 cnt - 2;
     * 2)如果敌人占用的点为割点,那么需要连的边数为 cnt;
     * 3)如果敌人占用的点只是某个连通块中顶点个数不为1的其中的一个点,那么需要
     * 连的边数为 cnt - 1;
     *
     * 此种做法确实比较繁琐,没有 直接去掉某个点,求出 去掉此点后的图中的连通块的
     * 数量,然后用数量减一就是所需连的边数,即答案 那么简便;但是前者的时间复杂
     * 度是线性的,而后者的时间复杂度是 O(K * M),是二次方复杂度,对于边数较大的题
     * 就有可能会超时。

    1. /**
    2. 时间戳:深度优先遍历时,访问每个节点的先后顺序从 1到n;
    3. 使用 num[] 来记录每个顶点的先后顺序;
    4. 满足割点的条件:子节点 v 不经过父节点 u 是不能达到u的祖宗节点的(也就是已经被访问
    5. 的那些节点);
    6. 使用 low[] 来记录每个节点不经过父节点能到达的最早顶点的时间戳;
    7. 因此满足割点的结点是: low[v] >= num[u] ;意思是子节点v不经过父节点u能到达的最早时间
    8. 戳也不过是u,那么把u去掉,不就能把图分成两部分了;但是这也仅仅是来检测非根节点的方法,
    9. 根节点是否为割点还要特判,如果根节点的子节点有两个孩子,且两个孩子不能相互到达,那么
    10. 根节点就是割点;
    11. */
    12. /**
    13. * 倒是也可以用割点算法来求解,但是割点是针对连通图而言,所以需要是对整个图的
    14. * 所有连通块都进行割点求解,然后再求解出整个图的连通块个数,并且把每个连通块
    15. * 的顶点个数求解出来,假定此时求出的连通块个数为cnt,
    16. * 1)如果有孤立的点存在,敌人把此点占用,那么连通块个数减一,所以需要连的边
    17. * 数为 cnt - 2;
    18. * 2)如果敌人占用的点为割点,那么需要连的边数为 cnt;
    19. * 3)如果敌人占用的点只是某个连通块中顶点个数不为1的其中的一个点,那么需要
    20. * 连的边数为 cnt - 1;
    21. *
    22. * 此种做法确实比较繁琐,没有 直接去掉某个点,求出 去掉此点后的图中的连通块的
    23. * 数量,然后用数量减一就是所需连的边数,即答案 那么简便;但是前者的时间复杂
    24. * 度是线性的,而后者的时间复杂度是 O(K * M),是二次方复杂度,对于边数较大的题
    25. * 就有可能会超时。
    26. */
    27. #include <iostream>
    28. #include <algorithm>
    29. #include <vector>
    30. using namespace std;
    31. const int maxn = 1e5+10;
    32. vector<int> Adj[maxn]; ///邻接表
    33. int num[maxn] , low[maxn] ; ///时间戳
    34. int flag[maxn]; ///顶点编号是割点则记为1,否则为0
    35. int root , index; ///root为根节点,index为时间戳
    36. void dfs(int u,int fath) ///fath为u的父节点
    37. {
    38. int child = 0; ///u的子节点数目
    39. ++index; ///时间戳加一
    40. num[u] = index;
    41. low[u] = index; ///最开始时间戳就是自己
    42. for(int i=0;i<Adj[u].size();++i)
    43. {
    44. int v = Adj[u][i];
    45. if(num[v] == 0) ///如果v还没被访问过
    46. {
    47. ++child; ///将v归为u的子节点,子节点数目加一
    48. dfs(v,u); ///递归
    49. low[u] = min(low[u] , low[v]); ///要将u的时间戳(是low,不是num)更新
    50. if(u != root && low[v] >= num[u]) ///如果u不是根节点,并且满足割点的条件
    51. {
    52. flag[u] = 1;
    53. }
    54. ///如果u是根节点且孩子节点也有两个,那么根节点就是割点;
    55. ///并且我们能证明出两个孩子节点一定不能通过若干结点中转相互到达;
    56. ///假设能相互到达,那么这两个孩子节点一定不能共同车成为u的孩子节点,
    57. ///因为一个孩子节点进行递归以后,会把该孩子节点能够连通且没有被访问的结点
    58. ///进行访问;
    59. if(u == root && child == 2)
    60. {
    61. flag[u] = 1;
    62. }
    63. }
    64. ///如果v已经被访问过且是u的祖宗节点(并不是父节点),则更新当前节点 u 能否
    65. ///访问到最早顶点的时间戳;
    66. else if(v != fath)
    67. low[u] = min(low[u] , num[v]);
    68. }
    69. }
    70. int id[maxn], Size[maxn];
    71. int stk[maxn];
    72. bool hs[maxn];
    73. int cnt, top;
    74. void FloodFill(int u)
    75. {
    76. hs[u] = 1;
    77. stk[top++] = u; //将u入栈,在栈中的所有顶点都是属于同一个连通块中;
    78. for(auto v : Adj[u])
    79. if(hs[v] == 0)
    80. FloodFill(v);
    81. }
    82. int main()
    83. {
    84. int n,m, k;
    85. cin >> n >> m >> k;
    86. for(int i=0;i<m;++i)
    87. {
    88. int u,v;
    89. cin >> u >> v;
    90. Adj[u].push_back(v);
    91. Adj[v].push_back(u);
    92. }
    93. for(int i=1 ;i<=n; ++i)
    94. if(hs[i] == 0)
    95. {
    96. FloodFill(i);
    97. ++cnt;
    98. int y;
    99. do{
    100. y = stk[--top];
    101. id[y] = cnt; //顶点编号属于cnt这个连通块
    102. Size[cnt]++; //cnt连通块个数加一
    103. }while(y != i);
    104. }
    105. for(int i=1; i<=n; ++i)
    106. {
    107. root = i;
    108. if(num[i] == 0)
    109. dfs(i,-1); //求割点
    110. }
    111. while(k--)
    112. {
    113. int chk;
    114. cin >> chk;
    115. if(flag[chk]) cout << cnt << endl; //割点
    116. else if(Size[id[chk]] == 1) cout << cnt - 2 << endl; //孤立的点
    117. else cout << cnt - 1 << endl;
    118. }
    119. return 0;
    120. }

  • 相关阅读:
    箭头函数
    微服务项目:尚融宝(45)(核心业务流程:借款申请(2))
    基于微信小程序的药店管理系统设计与实现-计算机毕业设计源码+LW文档
    Wnmp服务安装并结合内网穿透实现公网远程访问——“cpolar内网穿透”
    研发项目管理改进方法有哪些
    【自然语言处理】【向量检索】面向开发域稠密检索的多视角文档表示学习
    CSDN一站式云服务开放内测,诚邀C站新老用户来抢鲜
    Linux学习-71-GRUB手动安装方法
    Golang 笔试面试学习第一天之defer
    c++ 二分查找
  • 原文地址:https://blog.csdn.net/qq_51825761/article/details/127951628