蓝桥杯 试题 历届试题 国王的烦恼 并查集
问题描述
C国由n个小岛组成,为了方便小岛之间联络,C国在小岛间建立了m座大桥,每座大桥连接两座小岛。两个小岛间可能存在多座桥连接。然而,由于海水冲刷,有一些大桥面临着不能使用的危险。
如果两个小岛间的所有大桥都不能使用,则这两座小岛就不能直接到达了。然而,只要这两座小岛的居民能通过其他的桥或者其他的小岛互相到达,他们就会安然无事。但是,如果前一天两个小岛之间还有方法可以到达,后一天却不能到达了,居民们就会一起抗议。
现在C国的国王已经知道了每座桥能使用的天数,超过这个天数就不能使用了。现在他想知道居民们会有多少天进行抗议。
如果两个小岛间的所有大桥都不能使用,则这两座小岛就不能直接到达了。然而,只要这两座小岛的居民能通过其他的桥或者其他的小岛互相到达,他们就会安然无事。但是,如果前一天两个小岛之间还有方法可以到达,后一天却不能到达了,居民们就会一起抗议。
现在C国的国王已经知道了每座桥能使用的天数,超过这个天数就不能使用了。现在他想知道居民们会有多少天进行抗议。
输入格式
输入的第一行包含两个整数n, m,分别表示小岛的个数和桥的数量。
接下来m行,每行三个整数a, b, t,分别表示该座桥连接a号和b号两个小岛,能使用t天。小岛的编号从1开始递增。
接下来m行,每行三个整数a, b, t,分别表示该座桥连接a号和b号两个小岛,能使用t天。小岛的编号从1开始递增。
输出格式
输出一个整数,表示居民们会抗议的天数。
样例输入
4 4
1 2 2
1 3 2
2 3 1
3 4 3
1 2 2
1 3 2
2 3 1
3 4 3
样例输出
2
样例说明
第一天后2和3之间的桥不能使用,不影响。
第二天后1和2之间,以及1和3之间的桥不能使用,居民们会抗议。
第三天后3和4之间的桥不能使用,居民们会抗议。
第二天后1和2之间,以及1和3之间的桥不能使用,居民们会抗议。
第三天后3和4之间的桥不能使用,居民们会抗议。
数据规模和约定
对于30%的数据,1<=n<=20,1<=m<=100;
对于50%的数据,1<=n<=500,1<=m<=10000;
对于50%的数据,1<=n<=500,1<=m<=10000;
对于100%的数据,1<=n<=10000,1<=m<=100000,1<=a, b<=n, 1<=t<=100000。
解题思路:问题涉及两点的联通与否,首先想到并查集。居民抗议的条件是“如果前一天两个小岛之间还有方法可以到达,后一天却不能到达了,居民们就会一
起抗议“即两点状态由联通变为不连通。但是如果以小桥不能使用天数从小到达加入两点并查集并判断是否两点状态由不联通至联通会发生错误:"然而,只要这两座
小岛的居民能通过其他的桥或者其他的小岛互相到达,他们就会安然无事。",而并查集也没有将两点连通性“删除”的操作。这时可以使用逆向思维:由天数大至小
判断两点,若两点不连通,在并查集中加入两点后联通则居民抗议。桥不能使用 <---> 桥能使用 从联通至不连通 <--->从不连通至联通 天数从小到大 <--->从大到小
即用户抗议的条件是联通状态的改变,而状态改变的关键是天数大的桥:若一个桥在第4填建立后联通,则在第1-3填建立桥最多仍然联通。
实现代码:
1 #include<cstdio> 2 #include<algorithm> 3 using namespace std; 4 5 const int Max_N = 10000; 6 const int Max_M = 100000; 7 8 typedef struct 9 { 10 int a; 11 int b; 12 int t; 13 }Brige; 14 15 //输入 16 int n,m; 17 Brige brige[Max_M]; 18 19 //并查集 20 int par[Max_N+1]; 21 int rank[Max_N+1]; 22 23 /***并查集操作***/ 24 //初始化 25 void init( int n ) 26 { 27 for( int i=1; i<=n; i++ ) 28 { 29 par[i] = i; 30 rank[i] = 0; 31 } 32 } 33 //找某个节点的根 + 压缩路径 34 int find( int x ) 35 { 36 return x==par[x] ? x : par[x] = find(par[x]); 37 } 38 //合并两个节点 39 void unite( int x,int y ) 40 { 41 x = find(x); y = find(y); 42 43 if( x==y ) 44 return; 45 46 if( rank[x]<rank[y] ){ 47 par[x] = y; 48 } 49 else{ 50 par[y] = x; 51 if( rank[x]==rank[y] ) rank[x]++; 52 } 53 } 54 //判断两个节点是否同组(根节点相同) 55 bool same( int x,int y ) 56 { 57 return find(x)==find(y); 58 } 59 /***并查集操作***/ 60 //sort(结构体): 加入参数bool函数 61 bool cmp(Brige b,Brige b_) 62 { 63 return b.t>b_.t; 64 } 65 66 void solve() 67 { 68 init(n); //初始化操作 不能缺少 69 sort(brige,brige+m,cmp);//按天数从大至小排序 70 71 int res = 0; //计数 72 int day = -1; //同一天居民抗议次数最多+1 73 for( int i=0; i<m; i++ ) 74 { 75 if( brige[i].t!=day && !same(brige[i].a,brige[i].b) ) 76 { 77 res++; 78 day = brige[i].t; 79 } 80 unite(brige[i].a,brige[i].b); //"建立"桥(a,b) 81 } 82 printf("%d\n",res); 83 } 84 85 int main() 86 { 87 scanf("%d%d",&n,&m); 88 for( int i=0; i<m; i++ ){ 89 scanf("%d%d%d",&brige[i].a,&brige[i].b,&brige[i].t); 90 } 91 92 solve(); 93 94 return 0; 95 }