2021-面向对象设计与构造-第三单元总结
第九次作业
架构与设计策略
架构上,完全照搬给出的接口,对于每一个接口 (或异常抽象类) A
创建了 MyA
将其实现 (或继承)。
三次作业都是这么做的,因此不在后两次作业中进行赘述了。
绝大部分方法只需复制规格,异常类的实现使用 static
成员变量记录每个 id
的出现次数即可。
三次作业异常类大致相同,因此不在后两次作业中进行赘述了。
无向图中添加点、添加边、查询任意两点间是否连通、查询连通块数量,可以使用路径压缩+启发式合并的并查集,单次操作 \(O(\alpha(n))\) 或 \(O(1)\) 的复杂度内完成。
因为 id
是 int
范围内且强制在线,需要使用 HashMap
存储,时间上还要乘以一个小常数,不过问题不大。
查询 name
的排名直接复制规格进行暴力,单次复杂度不超过 \(O(|s|n)\),其中 \(|s|\) 是串长。写平衡树可以优化到 \(O(|s| \log n)\),但这么小数据范围根本没必要。
测试与 bug 分析
因为功能过于简单,主要采用随机生成数据和室友对拍的方法。同时构造了一些极端数据防止算法写假。
强测与互测中没有出现 bug,互测中主要采取看代码+构造的方法 hack 了 \(3\) 个人,hack 内容见下面的性能分析。
容器选择
MyNetwork
中使用 HashMap
存储每个 Person
,以及并查集中的 fa
数组与 siz
数组,键均为 id
。
MyPerson
中使用 HashMap
存储边的权值,键为 id
。
异常类中使用 HashMap
存储每个 id
的出现次数,键为 id
。
性能分析
我的实现中,复杂度瓶颈在于查询 name
的排名。
此外,查询连通块数量的 JML
规格是以 \(O(n^2)\) 次调用 isCircle
方法给出的,如果直接照搬规格, isCircle
的实现又比较慢,就可以被卡成 TLE,互测中也是根据这个问题进行 hack。
第十次作业
架构与设计策略
架构上,完全照搬给出的接口,对于每一个接口 (或异常抽象类) A
创建了 MyA
将其实现 (或继承)。
对于 Group
的 valueSum
,每次新增 Person
或删除 Person
时维护改变量,单次修改复杂度 \(O(n)\),单次查询复杂度 \(O(1)\)。
对于 Group
的 ageMean
和 ageVar
,通过维护 age
的和与平方和可以做到单次 \(O(1)\) 询问与修改,但是这并不是复杂度瓶颈,索性直接 \(O(n)\) 暴力。
剩下的方法就没有什么优化余地了,直接照搬规格即可。
需要注意的是以下两个坑:
valueSum
是有序的,每条边的权值要算两次。- 一个人可能在好几个组里,
addRelation
时要考虑将对应边的权值加到对应的Group
中。
测试与 bug 分析
因为功能过于简单,主要采用随机生成数据和室友对拍的方法。同时构造了一些极端数据防止算法写假。
强测与互测中没有出现 bug,互测中主要采取看代码+构造的方法 hack 了 \(2\) 个人,hack 内容见下面的性能分析。
组内另有一人使用了 ArrayList
而非 HashMap
,看代码时完全忽略,没有针对其进行构造,错失了这个机会。
容器选择
MyNetwork
中使用 HashMap
存储每个 Group
与 Message
,键值为对应 id
。
MyPerson
中使用 ArrayList
存储他收到的信息。
MyGroup
中使用 HashMap
存储其中的 Person
,键值为 id
。
性能分析
Group
中查询 valueSum
的 JML
规格是以 \(O(n^2)\) 次调用 isLinked
方法给出的,如果直接照搬规格,可以被卡成 \(O(n^3)\) 从而造成 TLE,互测中也是根据这个问题进行 hack。
第十一次作业
架构与设计策略
架构上,完全照搬给出的接口,对于每一个接口 (或异常抽象类) A
创建了 MyA
将其实现 (或继承)。
除了最短路,其它部分的规格都是单纯模拟,没有优化余地。
对于无负边权的单源最短路,使用优先队列实现的堆优化 Dijkstra 算法可以在 \(O(m \log m)\) 的复杂度内完成。
测试与 bug 分析
因为功能过于简单,主要采用随机生成数据和室友对拍的方法。同时构造了一些极端数据防止算法写假。
强测与互测中没有出现 bug,互测中主要采取看代码+构造的方法进行 hack,但大家写的都很对,因此没有 hack 成功。
容器选择
MyNetwork
中使用 HashMap
实现规格中的 emojiHeatList
。
MyNetwork
中利用 PriorityQueue
实现了堆优化 Dijkstra 算法。
性能分析
在本题的数据范围和 \(6\) 秒时限下,单次最短路询问 \(O(m \log m)\) 是可以通过的。
不过,需要注意 Dijkstra 的暴力算法并非毫无用途,其复杂度为 \(O(n^2)\),在稠密图上快于堆优化,例如这个题 CF1528D。
总结与收获
-
感谢课程组为我们减负,三次作业简直是从头暴力到尾,老爽了。
当然,一个缺点就是写博客不知道写啥,理论课上老师说要我们多写点儿,但是我觉得真的没啥好写的,如果助教审阅时觉得字数太少我可以再水一些(逃。 -
Java8 的
HashMap
非常人性化,当一个哈希值的元素过多时会将链表转化为红黑树,元素过少时又会将红黑树转化为链表,降低了哈希冲突的代价,也让我断绝了我卡哈希的想法。 -
理论课上老师多次提出:「形式化方法工程应用受限。」
这三次作业让我深刻体会到了这一点。一些方法寥寥几行就能写完,形式化规格却要写的特别臃肿冗长。如果说这么做的代价是能进行形式化验证,那么意味着可以做到严格意义上的无 bug,简直再好不过了。但是现实是还需要自己造数据进行测试,那「形式化」就会成为效率的阻碍。
希望以后能看到形式化验证在工程上广泛应用的那一天,
这样就再也不用担心没找出来的 bug 了。