注意:2018年复试题目由于一些
不可告人的原因,无法公布。目前只可以放出来第三题。此复试题目是根据博客园博主林木子发布的博客发布的博客整理复原。为了恢复试题的原貌,我根据试题要求进行合理的脑补,按照oj系统的风格补全了试题的Problem Description、Input、Output、Sample Input、Sample Out等内容,并加入了详解、具体的代码实现。因为是复原版,不能保证当时的题目就是这么呈现的,也不能说明考场上的试题就是oj风格叙述的,仅供大家参考。有什么错误、不合理的地方欢迎指出。
原创不易,还请大家多支持
第三题
Problem Description
Input
1<=N<=300
;1<=wi<=100000
;p(i,i)=0
;1<=p(i,j)=p(j,i)<=100000
。Output
Sample Input
5
4
4
3
1
20
0 2 2 2 9 9
2 0 3 3 9 9
2 3 0 4 9 9
2 3 4 0 9 9
9 9 9 9 0 9
9 9 9 9 9 0
Sample Output
刚拿到这道题的时候我感觉是在考察最短路径问题,但若看做是最短路径就会有无法处理打井这一情况的问题。所以,我们可以把此问题抽象为求最小生成树,但不是传统意义上的最小生成树。首先我们需要对条件进行处理,把这n块地看成一张图中的n个点,把在某处打井也看做一个点,这样图中就有了2n个点。以样例输入为例,如图:数字表示n个地块,数字附近的蓝色点表示可在该地块打井,加起来共有12个点。然后我们考虑这个特殊图的最小生成树的求法。
使用Kruskal 算法的思路求解最小生成树,Kruskal 求解一般图的最小生成树的方法是始终选择权值最小的一条边,并把联通的点纳入集合,当集合中纳入所有的点时就求得了最小生成树。这里我们只需修改Kruskal 算法中把点纳入集合的策略就可达到我们的目的。首先,将所有地块打井的费用看做是图中的一条边,现在,图中共有6+6*5/2=21条边。其中还有一些边暂时不符合我们的要求,因为两地之间若要有边联通必然有一地有水源,所以两地均没有水源的边我们暂时先不考虑。
为了达到这一目的,我们可以使用一个变量保存目前可选的边,并从中选择一个权值(花费)最小的边,并把已有水源的点从保存没有水源的集合中去除,当集合中没有元素的时候,就得到了最小生成树,即得到了最小花费。下图是整个流程。
此代码已在洛谷P1550 USACO08OCT打井Watering Hole AC
1 |
|
为了方便快捷高效率的求得所有可选边中权值最小的,我们需要使用堆数据结构。它可以以 O(logn)
的复杂度取得最小元素。为了绕过对堆的实现,我们使用标准模板库中的相应的标准模板——优先队列。利用语句priority_queue<Way> temp;
建立一个保存元素为Way
的堆temp
,但是请特别注意这样建立的堆其默认为大
顶堆,即我们从堆顶取得的元素为整个堆中最大的元素。而我们恰恰需要取得堆中最小的元素,于是我们使用如下语句定义一个小顶堆:priority_queue<Way, vector<Way>, greater<Way>> temp;
其次,为了方便确定某地块有没有通水源,使用了运用hash
的map
数据类型,我们可以直接通过地块号来确定是否有水源