蓝桥杯真题 国王的烦恼PREV-22
由于原题过于复杂,这里我简要概括一下题目
一个国家有N个小岛,岛屿之间用桥连接起来
每座桥都有使用寿命,使用寿命到期后桥就会坏掉不能通行
存在任意的岛A和岛B,如果第n天可以从岛A到达岛B(可以绕道),第n+1天无法从岛A到达岛B(n>=1)
那么居民就会抗议,一天只会抗议一次
原题输入格式
输入的第一行包含两个整数n, m,分别表示小岛的个数和桥的数量。
接下来m行,每行三个整数a, b, t,分别表示该座桥连接a号和b号两个小岛,能使用t天。小岛的编号从1开始递增。
原题输出格式
输出一个整数,表示居民们会抗议的天数。
题意分析
一个无向图,每过一天删去一些边。
如果连通情况发生变化,输出值加1。
输入一个边集数组,输出居民抗议的次数。
隐藏条件:本题两岛之间最多只有一个桥,题意并看不出来,不过即便有重复的也很好处理(需要处理,本题不用)。
暴力法
我们先简要说一下暴力法,之后我们在讨论本题解法
第一步
输入边集数组并排序。可以选择是否构造其它结构的图便于遍历。
第二步
遍历图,无论是深搜还是广搜都无所谓,重要的是保存当前所有连通分量的状态,这也是本题暴力法的难点,因为对于大量数据来说连通分量的数量可能是不断变化的,以及连通分量的状态也是不断变化的,表示这些不同的连通分量又要费一番功夫。
如果当前连通分量的状态不等于上一次连通分量的状态村民抗议1次。
第三步
删除一些边(即当天损坏的桥),重复第二步。
我本人能想到的暴力法思路就是这样,下面我们说说并查集
解题方法:并查集
简单说一下并查集,并查集是一种树型数据结构,在解决部分图问题是格外有效。
并查集的概念就是找到有关系的数据,并把它保存到一个集合中,用根结点表明这个集合。
其实与其说是树形,这个树的终极形态基本上只有根结点和叶子结点。
通过并查集我们把不同的集合合并到一个集合中,在应用过程中也存在滞留处于森林状态的情况。
以后我可能单独开篇讲并查集,这里不做过多的解释了。以本题为重点!
并查集解题思路
逆向思维,我们不能总是顺着题意去想第一天过了会发生什么。
我们让时光倒流,即按照逆序建桥。 //对这个算法有疑惑时,我们要时刻记得这句话。
在建桥过程中我们看看这次建桥是不是使得原本不相关的两个岛或区域连通了,即并查集中的根结点是否相同。
通过逆向思维,我们发现在题意中我们创建的这个桥在损坏后,就会造成两个区域不连通。
我们回到时光倒流中,此时我们建立了一个桥使得两个区域连通,我们要做的就是把一个区域的根结点指向另一个区域的根节点,这样我们就可以判定他们属于一个连通区域啦!(注意一天最多抗议1次)
核心代码
由于上全部源码不方便这里就把核心源码提供出来给大家参考,代码后面还有解析
边集结点和并查集数组
处理数据
核心算法
样例解析
因为原题给了一个样例特别的简单不能说明问题。
下面是我自己想的一个示例
输入数据
注意这里我对它进行了预排序,我们将不再重新sort,因为这样排序更能说明问题。
示例图
如果搞不懂这个算法的话,可以按照程序顺序自己把这个程序手算一遍
1、3~4,结果roots[3]=4; //此时3的根3和4的根4不相等,证明决定连通,抗议一次
2、5~6,结果roots[5]=6; //此时5和6不相等,但一天只抗议一次
3、3~5,结果roots[4]=6; //此时find(3)=4,find(5)=6,不相等,这个桥决定两个区域的连通,只需要一个根指向另一个根,把他们划分到同一个集合中。
4、1~3,结果roots[3]=6、root[1]=6; //此时root[3]=6是在递归过程中赋值的,即便root[3]的值不变也不会影响计算结果因为find是递归函数,最终会找到根结点、至于岛1第一次出现,自然归并到大集合中,本例对天数概念弱化,因为天数不是难点。
5、2~5,结果roots[2]=6;
6、1~2,此时find(1)和find(2)的结果相同均是6表明,没有桥1和2,其余的图仍然连通,即村民不抗议。
所以本例只在第二天和第三天抗议了一次