BUAA面向对象2022第三单元总结
总览
作业内容
本单元的主要作业内容为读JML并实现一个Network,实际上就是对图的一些操作。共有3次作业,为迭代开发。
第一次作业需要实现加人,加关系,求关系图可达性,求关系图连通块个数以及一些异常情况的处理。
第二次作业增加了发消息,求群成员各种属性,以及求关系图最小生成树和消息相关的异常。
第三次作业加入多种不同类型的消息的处理,以及一个间接消息发送(求最短路)。
完成情况
第一次作业没有错误。
第二次作业没有看到群内最多1111人,互测被干了。
第三次作业给群发红包后发送者的钱算错了,互测被干了 (这强测没错我不能理解) 。
互测情况:三次作业 发起/受到hack 分别为 0/0 , 4/6 , 0/1 。
hack了两个qgvs写的太丑tle的。
实现过程
总体思路
其实大部分按照JML写就行。然后就是怎么方便怎么来,JML规格里的数组可以用HashMap来实现,这样就可以快速通过id找到对应的人/组了。
难点
有几个方法可能不能完全按照JML的规格来写,需要一些简化。
isCircle:判断关系图中两人是否可达,用并查集即可,顺便可以把queryBlockSum也维护了。
queryLeastConnection:最小生成树,直接跑一遍prim就行。
queryGroupValueSum:这个不能用两层循环,否则会TLE,应该遍历边。
sendIndirectMessage:需要求一个最短路,跑一边dij就行。
总体来说就是保证每次复杂度都在O(N)这个级别就行,qlc和sim因为次数有限制,可以多带个log。
毕竟不是算法课,就懒得去优化复杂度了。
扩展内容
新建Product类,有id,name,price等属性。
新建Advertiser,Producer,Customer类分别继承Person类。
新建Advertisement类, 有id,productId ,producer等属性。
NetWork中增加products,advertisements数组。
下面是三个关键方法(produce:生产物品,advertise:发送广告,buyProduct:购买物品)的JML。
为了简便,忽略了id不存在,id不对应,id重复等异常。
/*@ public normal_behavior
@ assignable products;
@ ensures products.length == \old(products.length) + 1;
@ ensures (\forall int i; 0 <= i && i < \old(products.length);
@ (\exists int j; 0 <= j && j < products.length; products[j] == (\old(products[i]))));
@ ensures (\exists int i; 0 <= i && i < products.length; products[i] == product);
*/
public void produce(/* non_null */ Product product);
/*@ public normal_behavior
@ assignable advertisenments;
@ ensures advertisenments.length == \old(advertisenments.length) + 1;
@ ensures (\forall int i; 0 <= i && i < \old(advertisenments.length);
@ (\exists int j; 0 <= j && j < advertisenments.length; advertisenments[j] == (\old(advertisenments[i]))));
@ ensures (\exists int i; 0 <= i && i < advertisenments.length; advertisenments[i] == advertisenment);
*/
public void advertise(/* non_null */ Advertisement advertisement);
/*@ public normal_behavior
@ assignable getPerson(custId).money,getPerson(getAdv(advId).getProducer()).money;
@ assignable products;
@ ensures !containsProduct(getAdv(advId).getProductId()) && products.length == \old(products.length) - 1 &&
@ (\forall int i; 0 <= i && i < \old(products.length) && \old(products[i].getId()) != getAdv(advId).getProductId();
@ (\exists int j; 0 <= j && j < products.length; products[j].equals(\old(products[i]))));
@ ensures getPerson(custId).money = \old(getPerson(custId).money) - getProduct(getAdv(advId).getProductId()).getPrice();
@ ensures getPerson(getAdv(advId).getProducer()).money = \old(getPerson(getAdv(advId).getProducer()).money) + getProduct(getAdv(advId).getProductId).getPrice();
*/
public void buy(int custId, int advId)
总结与反思
List有个removeIf方法挺好用的,避免了边遍历边删除的尴尬处境,也不用写很多行代码。
Java的HashMap很高级,Hash冲突多了会用平衡树存同一个hash值的玩意,不能卡成线性。
还是要认真读题。