蓝桥杯 试题 历届试题 危险系数 并查集/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
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\n"); 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\n",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 }
- 另一个思路是用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\n"); 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\n",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 }
- 只要用一个数组将经过节点保存,并记录数组长度,当到达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\n"); 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\n",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 }