zoukankan      html  css  js  c++  java
  • 蓝桥杯 试题 历届试题 危险系数 并查集/dfs

    问题描述

    抗日战争时期,冀中平原的地道战曾发挥重要作用。

    地道的多个站点间有通道连接,形成了庞大的网络。但也有隐患,当敌人发现了某个站点后,其它站点间可能因此会失去联系。

    我们来定义一个危险系数DF(x,y):

    对于两个站点x和y (x != y), 如果能找到一个站点z,当z被敌人破坏后,x和y不连通,那么我们称z为关于x,y的关键点。相应的,对于任意一对站点x和y,危险系数DF(x,y)就表示为这两点之间的关键点个数。

    本题的任务是:已知网络结构,求两站点之间的危险系数。

    输入格式

    输入数据第一行包含2个整数n(2 <= n <= 1000), m(0 <= m <= 2000),分别代表站点数,通道数;

    接下来m行,每行两个整数 u,v (1 <= u, v <= n; u != v)代表一条通道;

    最后1行,两个数u,v,代表询问两点之间的危险系数DF(u, v)。

    输出格式
    一个整数,如果询问的两点不连通则输出-1.
    样例输入
    7 6
    1 3
    2 3
    3 4
    3 5
    4 5
    5 6
    1 6
    样例输出
    2
    解题思路:
    • 题目即求割点。因为并查集可以判断两点是否联通(类似于蓝桥杯发现环:https://www.cnblogs.com/w-like-code/p/12934398.html),所以一个思路是依次去掉通道,判断此时u,v是否联通,

    若不联通说明与此边的点为关键点。注意让避免重复计算某点,且要排除u,v。

    • 实现代码:
      1 #include<cstdio>
      2 
      3 const int Max_N = 1000;
      4 const int Max_M = 2000;
      5 
      6 int n,m;
      7 int U[Max_M],V[Max_M]; //保存通道两点 
      8 int u,v; //待查询 
      9 
     10 int par[Max_N+1];
     11 int rank[Max_N+1];
     12 
     13 bool used[Max_N+1];
     14 
     15 void init( int n )
     16 {
     17     for( int i=1; i<=n; i++ )
     18     {
     19         par[i] = i;
     20         rank[i] = 0;
     21     }
     22 }
     23 
     24 int find( int x )
     25 {
     26     if( par[x]==x ){
     27         return x;
     28     }
     29     return par[x] = find( par[x] );
     30 }
     31 
     32 void unite( int x, int y)
     33 {
     34     x = find(x); y = find(y);
     35     if( x==y ){
     36         return;
     37     }
     38     if( rank[x]<rank[y] ){
     39         par[x] = y;
     40     }
     41     else{
     42         par[y] = x;
     43         if( rank[x]==rank[y] ){
     44             rank[x]++;
     45         }
     46     }
     47 }
     48 
     49 bool same( int x, int y )
     50 {
     51     return find(x)==find(y);
     52 }
     53 
     54 void solve()
     55 {
     56     //首先判断u,v是否联通  
     57     init( n );
     58     for( int i=0; i<m; i++ ){
     59         unite( U[i],V[i] );
     60     }
     61     if( !same(u,v) ){  
     62         printf("-1
    ");
     63         return;
     64     }
     65     
     66     //每次判处下标为i的通道 用并查集判断关键点 
     67     int res = 0;
     68     for( int i=0; i<m; i++ )
     69     {
     70         init(n);
     71         for( int j=0; j<m; j++ )
     72         {
     73             if( i==j )    continue;
     74             
     75             unite(U[j],V[j]);
     76         }
     77         if( !same(u,v) ){
     78             if( !used[U[i]] ){ //用used[]避免重复计算 
     79                 res++;
     80                 used[U[i]] = true;
     81             }
     82             if( !used[V[i]] ){
     83                 res++;
     84                 used[V[i]] = true;
     85             }
     86         }
     87     }
     88     if( used[u] )    res--;
     89     if( used[v] )    res--;
     90     
     91     printf("%d
    ",res);
     92 }
     93 
     94 int main()
     95 {
     96     scanf("%d%d",&n,&m);
     97     for( int i=0; i<m; i++ )
     98     {
     99         scanf("%d%d",&U[i],&V[i]);
    100     }
    101     scanf("%d%d",&u,&v);
    102     
    103     solve();
    104     
    105     return 0;
    106 }
    View Code
    • 另一个思路是用dfs判断从u到v的所有路径,而关键点就是每个u到v路径都要经过的点。
    • 实现代码: 首先思路是若dfs到达v,返回true,所有返回true的节点都+1。但可能出现:
    • 对于五角星割点,其只有一次返回true,计数为1,但实际上u-v路径数为2

    • 错误代码: 

     1 #include<cstdio>
     2 #include<vector>
     3 using namespace std;
     4 
     5 const int Max_N = 1000;
     6 const int Max_M = 2000;
     7 
     8 //输入 
     9 int n,m;
    10 int u,v;
    11 vector<int> G[Max_N+1]; //
    12 
    13 bool visited[Max_N+1]; //visited[i]:在当前dfs()中是否访问过i 
    14 int count[Max_N+1]; //count[i]:下标i出现在u-v路径中的次数 
    15 int sum; //计算所有路径数 
    16  
    17 bool dfs( int x )
    18 {
    19     if( x==v ){
    20         sum++;
    21         return true;
    22     }
    23     
    24     bool flag = false;
    25     for( int i=0; i<G[x].size(); i++ )
    26     {
    27         int x_ = G[x][i];
    28         if( !visited[x_] )
    29         {
    30             visited[x_] = true;
    31             if( dfs( x_ ) ){
    32                 count[x]++;
    33                 flag = true;
    34             }
    35             visited[x_] = false;    
    36         } 
    37     }
    38     return flag;
    39 }
    40 
    41 void solve()
    42 {    
    43     
    44     visited[u] = true;
    45     if( !dfs( u ) ){//若u,v不连通 
    46         printf("-1
    ");
    47         return;
    48     }
    49     
    50     int res = 0;
    51     for( int i=1; i<=n; i++ )
    52     {
    53         if( count[i]==sum ){
    54             res++;
    55         }
    56     }
    57     
    58     if( count[u]==sum )    res--;//多记录了u 
    59     
    60     printf("%d
    ",res); 
    61 }
    62 
    63 int main()
    64 {
    65     scanf("%d%d",&n,&m);
    66     for( int i=0; i<m; i++ )
    67     {
    68         int x,y;
    69         scanf("%d%d",&x,&y);
    70         G[x].push_back(y); G[y].push_back(x);
    71     }
    72     scanf("%d%d",&u,&v);
    73     
    74     solve();
    75     
    76     return 0;
    77 }
    View Code
    • 只要用一个数组将经过节点保存,并记录数组长度,当到达v时把数组中节点计数即可。
    • 正确代码:
     1 #include<cstdio>
     2 #include<vector>
     3 using namespace std;
     4 
     5 const int Max_N = 1000;
     6 const int Max_M = 2000;
     7 
     8 //输入 
     9 int n,m;
    10 int u,v;
    11 vector<int> G[Max_N+1]; //
    12 
    13 bool visited[Max_N+1]; //visited[i]:在当前dfs()中是否访问过i 
    14 int count[Max_N+1]; //count[i]:下标i出现在u-v路径中的次数 
    15 int sum; //计算所有路径数 
    16 
    17 int way[Max_N]; //记录路径节点 
    18  
    19 void dfs( int x , int index )
    20 {
    21     if( x==v ){
    22         sum++;
    23         for( int i=0; i<index; i++ ){
    24             count[way[i]]++;
    25         }
    26         return;
    27     }
    28     
    29     way[index] = x;
    30     for( int i=0; i<G[x].size(); i++ )
    31     {
    32         int x_ = G[x][i];
    33         if( !visited[x_] )
    34         {
    35             visited[x_] = true;
    36             dfs( x_, index+1 );
    37             visited[x_] = false;    
    38         } 
    39     }
    40 }
    41 
    42 void solve()
    43 {    
    44     
    45     visited[u] = true;
    46     dfs( u , 0 );
    47     
    48     if( count[u]==0 ){
    49         printf("-1
    ");
    50         return;
    51     }
    52     
    53     int res = 0;
    54     for( int i=1; i<=n; i++ )
    55     {
    56         if( count[i]==sum ){
    57             res++;
    58         }
    59     }
    60     
    61     printf("%d
    ",res-1); //多记录了节点u 
    62 }
    63 
    64 int main()
    65 {
    66     scanf("%d%d",&n,&m);
    67     for( int i=0; i<m; i++ )
    68     {
    69         int x,y;
    70         scanf("%d%d",&x,&y);
    71         G[x].push_back(y); G[y].push_back(x);
    72     }
    73     scanf("%d%d",&u,&v);
    74     
    75     solve();
    76     
    77     return 0;
    78 }
    View Code
  • 相关阅读:
    服务部署 RPC vs RESTful
    模拟浏览器之从 Selenium 到splinter
    windows程序设计 vs2012 新建win32项目
    ubuntu python 安装numpy,scipy.pandas.....
    vmvare 将主机的文件复制到虚拟机系统中 安装WMware tools
    ubuntu 修改root密码
    python 定义类 简单使用
    python 定义函数 两个文件调用函数
    python 定义函数 调用函数
    python windows 安装gensim
  • 原文地址:https://www.cnblogs.com/w-like-code/p/13906390.html
Copyright © 2011-2022 走看看