作为第二次3+1的总结,明显感受到了此次题目集越来越复杂,结合了实际的物理知识来解决现实中的电路问题。因为电路可以一直扩展下去,情况千变万化,难以像上次题目集一样找到一个呆板的做法。这次题目集,让很多人连题目都无法理解,代码也是无从下手,因为这些人根本不知道如何去设计,如何去完成电路上的整个过程,也就是没有抓到本质。
目前题目集只迭代了两次,从最开始的单一串联电路、单一设备,到现在的由多个串联电路组成的并联电路、多个设备,这两次迭代已经可以看出这次题目集的复杂性。再经历两次迭代后,将完整的模拟电路中可能出现的所有情况(多并联、多串联、短路、双向开关......),所以对我们前期的设计思路要求缜密且严谨。
而且在第二次迭代中,题目采取了不提示测试点的手段,让很多学生举步维艰,测试了N个由自己或同学创造的样例后仍然找不到错误,最终停留在了88、91、94分。由于学生代码设计上的缺陷以及题目不提示测试点的手段,使得很多人痛不欲生,”明明别人的样例我都过了,可为什么他拿到了100而我只有94呢?“,在这样的自我怀疑中不断地寻找错误。运气好的同学也许再测试了一些样例后可以找到错误,可运气不好的同学可能等到题目结束的最后一刻都没有找到一丁点错误,可是事实就是他错了,但是错得看起来天衣无缝,无法叫人发现。
并且我统计了前六次题目集得提交以及通过情况:
由此不难看出,题目集的实际通过率(通过人数/考试人数)处于0.2~0.5这个区间,其中最难的一次为题目集三,实际通过率仅为0.178,但提交数量却是最多的,三百人提交了将近一万三千份答卷。学生在经历了这么多次练习后,通过率的总体趋势却是起起伏伏,当然这也和题目难度相关。题目集一和五都是作为两次大迭代的开端,故而各个数据都非常相似,由此我们也许可以预测一下,下一次题目集的通过率可能会达到第二次迭代最低。
从上表可以看出,在经历了第三次题目集的大洗劫后,居然得到了很大的改善,不仅提交量减少了,而且提交通过率还提高了不少。也许是因为同学在第三次题目集下了很大的功夫还吃了亏,将代码的质量提高了不少。但这并不是今天的重点。
UML类图:
这次题目集便是第二次大迭代的开端,主要内容为计算单一串联电路上的设备状态
题目主干:
设计一个智能家居强电电路模拟系统:
模拟的控制设备包括:开关、分档调速器、连续调速器。
模拟的受控设备包括:灯、风扇。两种设备都有两根引脚,通过两根引脚电压的电压差驱动设备工作。
并且提供了四次迭代的区别:
迭代1:只有一条线路,所有元件串联
迭代2:线路中包含一个并联电路
迭代3:线路中包含多个串联起来的并联电路
迭代4:并联电路之间可能出现包含关系
这就很考验学生的总体设计能力,不能只顾眼前的题目集,还要考虑到为题目集的迭代做好准备。
设计思路:
因为这次题目的设备并没有电阻,所以无法使用正常的根据电阻求分压的方法来计算设备状态,也因此,电路中只能出现一个设备,不然出现多个设备的话无法进行分压操作。这一点也就给了很多人设计一个特殊电路的可乘之机。但是我并不是如此。
首先,我们判断电路开端是否含有VCC信息,如果有,就设置电路初始电压为220V,反之为0V,GND判断同理。
然后读取所有设备信息,包括控制器和被控制器。按照从电路开端到电路接地端的方向一步一步进行处理,并且令下一个设备的输出电压等于上一个设备的输出电压。这样就能得到所有设备的输入电压和输出电压。
其次,获取了输入电压和输出电压后,就能用前者与后者的差计算出该设备的电压,再根据该设备的状态计算方式计算出它的转速或者亮度等状态信息。
最后,对设备信息进行排序输出。到此,这道题目我们就解决了。
UML类图
遇到的困难:
按照刚才提供的设计思路可以解决大多数单一串联电路的情况,但是唯独有一种情况不能解决。
开关在受控设备后
因为,开关在受控设备后的时候会出现,受控设备已经获取到了输入和输出电压,但是开关却是断连的情况。但是受控设备的电压差已经计算出来了,也就是状态也被确定了。所以我们需要一些特殊处理。
遍历所有开关
确保所有开关都闭合再输出,如果有一个开关不闭合那就将受控设备的输入电压设定为0V,再进行输出。这样就能解决刚才的问题。
此次题目集,加入了并联电路,并联电路可以由多个串联电路组成。每一条串联电路上可以有多个设备,并且新增了一个设备(落地扇),还为每个设备都赋予了电阻这个属性。使得整个电路系统更加符合现实中的情景。正是因为加入了电阻,才让整条电路具有了嵌入多个设备的条件,因为这样我们才可以正常的去计算每个设备的分压。
设计思路:
我将这道题的解法分为八个部分:
第一步:读入所有信息
第二步:找到并联电路含有的所有串联电路,并处理串联电路:
假设该并联电路包含an条串联电路,那就从第a1条串联电路开始遍历这n条串联电路,如果该串联电路是连通的(所有开关都闭合),那就将该串联电路的连通度标记为1,否则标记为0,并遍历该串联电路上的所有设备,将有电阻的设备的阻值相加,得到总电阻。
第三步:将该并联电路转换为等效的串联电路,由于第二步已经获取了n条串联电路的总电阻,将所有连通度为1的串联电路的电阻倒数相加再取倒数,便能得到该并联电路的等效电阻。
第四步:遍历该总电路上的所有设备(包括并联电路),将所有设备的电阻值相加,得到总电路电阻。
第五步:再遍历该总电路上的所有设备(包括并联电路),将所有设备的电阻值与总电路电阻求比,并与总电路电压相乘,得到每个设备的分压。
第六步:再一次找到并联电路含有的所有串联电路,并处理串联电路中的所有设备:
由于第二步我们已经得到了n条串联电路的总电阻,所以此时遍历所有设备求其电阻与总电阻之比,并与第五步得到的并联电路分压相乘,得到并联电路上的串联电路的各个设备的分压。
第七步:根据得到的每个设备的分压计算其对应的状态(转速、亮度)。
第八步:对每个设备进行排序,并输出。
在上面的步骤中,体现出要想获取分压,得先获得各条串联电路的总电阻。也就是设备分压和总电阻不可能在一次循环中就能被获取。所以每一条电路我们都需要走上两遍,并且存在在样的规律:
遍历次数=总电路数×2+并联电路数×2
UML类图:
遇到的困难:
困难居然出在排序上,好吧,我确实不清楚如何让它们按照开关、分档调速器、连续调速器、白炽灯、日光灯、吊扇、落地扇的顺序依次输出。但是我可以换个方法,我先让所有设备按照序号排序。
controlledDevices.sort(Comparator.comparingInt(ControlledDevice::getNumber));
devices.sort(Comparator.comparingInt(Device::getNumber));
让它们的顺序变得有序,但是名称是无序的,这样就够了。
比如,设备信息原始的顺序是:
D2,A1,D4,D3,D1,A4,B3,A2,B1,B4,B2,A3
那么排序后的顺序应该是这样的:
(A1|D1|B1),(A2|D2|B2),(A3|D3|B3),(A4|D4|B4)
其中“|”表示前后顺序随机
这个时候,只需要一点暴力的手段。写上几个循环,每一次都完整的把设备都遍历一遍,并且判断设备总类是否是需要输出的,如果是就输出,如果不是,就不输出。这样调整不同设备的循环的位置,就可以实现对名称排序了。挺笨的了
😢
但是能实现这个功能吧,也不是毫无用处。
代码分析:
Class | OCavg | OCmax | WMC |
---|---|---|---|
ControlledDevice | 1 | 1 | 2 |
Controller | 1 | 1 | 4 |
Device | 1 | 1 | 18 |
Fan | 1 | 1 | 4 |
FanOfA | 2 | 5 | 8 |
FanOfD | 1.5 | 3 | 6 |
FilamentLamp | 1.67 | 3 | 5 |
FluorescentLamp | 1.33 | 2 | 4 |
Lamp | 1 | 1 | 3 |
ParallelCircuit | 1 | 1 | 6 |
SeriesCircuit | 1 | 1 | 4 |
SwitchOfF | 2.2 | 7 | 11 |
SwitchOfK | 1.2 | 2 | 6 |
SwitchOfL | 1 | 1 | 5 |
TotalCircuit | 1 | 1 | 14 |
Package | v(G)avg | v(G)tot |
---|---|---|
3.25 | 276 | |
Module | v(G)avg | v(G)tot |
newpta6 | 3.25 | 276 |
Project | v(G)avg | v(G)tot |
project | 3.25 | 276 |
好吧,惨不忍睹。
以下是一些投机取巧
在改变开关状态的时候,我并没有使用布尔变量来表示开关的开闭。而是使用int类型的数据。首先给开关的状态赋值为0,之后每遇到同一个开关的输入信息,状态的赋值就加1,在最后判断开关状态时,对该值模2取余,如果余数为0,那开关就是断开的,如果余数为1,那开关就是闭合的。
电路系统中的每一种电路我都赋予了它一种属性——是否连通:
串联电路上的每一个开关闭合则连通;
并联电路上的只要存在一个串联电路连通,那并联电路也联通;
总电路上的所有开关闭合且并联电路连通,那总电路也连通。
连通则赋值为1,不连通则赋值为0。所以判断并联电路是否连通只需要把该并联电路包含的所有串联电路的连通度相加,若为0则为不连通,若大于0则为连通。
我为什么这么做呢?
因为这样的话,我可以把所有的开关一起等效为一个连接在总电路上的总开关,只有这个总开关闭合(也就是并联电路和总电路都是连通的),总电路才是联通的。
以上是一些投机取巧
又是三次题目集,我好像敲代码的能力是提高了。设计能力稍有改善,但还是捉襟见肘。现在的代码量都已经上千行了,却还是没有一个很好的设计能力。到底是什么耽误了我的学习,Java课我也认真听,我也会上网课,虽然我可以拿到题目的满分,但是如果对代码也有评分的话,那我估计得完蛋。看来我还需要多学一些设计的思想,很多东西要上手了才会知道怎么用。我感觉我一直都在写很基础的代码。好的工具不用,怎么干得了好活呢?
我觉得题目里面有句话很模糊,但是又没有错,感觉很多人都是被这句话给迷惑了:
只要不造成短路而产生无穷大的电流烧坏电路,都是合理情况,在测试点的考虑范围之内。本次大作业不考虑短路的情况,测试点中不包括短路的电路。
对于这段话,请看下面这个样例:
#T1:[IN K1-1] [K1-2 D2-1] [D2-2 OUT]
#T2:[IN K2-1] [K2-2 D1-1] [D1-2 OUT]
#M1:[T1 T2]
#T3:[VCC M1-IN] [M1-OUT A3-1] [A3-2 GND]
#K2
#K1
end
很明显,这个样例中的并联电路处于短路状态呢,那是不是就不考虑这种情况了?不要忘了,不考虑短路的情况还包含一个前提条件:
产生无穷大的电流烧坏电路。
请问,这会产生无穷大的电流吗?会烧坏电路吗?
当然不会
这个是短接,属于短路的一种。被短接的部分就相当于是导线了,也就是该并联电路的等效电阻为0欧姆,就不会产生分压,那何来烧坏电路之说呢?
我并不知道测试点里有没有这个情况,但是我觉得我的理解很到位。
还有,我开始居然想用输入引脚和输出引脚的电势差来计算分压,然后发现真的是大可不必,前面的方法是用来应对电路模拟程序-1的,现在已经有电阻了,直接计算分压才是正解!
本次题目给的样例只有3个,然后测试点毫无提示。这真的是很折磨也很费头发的两个因素,想来很多人差两三个没过的原因就在此处了吧。毕竟完全靠自己摸爬滚打,没有任何提示,所有人都一样。所以,我就疯狂写样例,前前后后用了不下五十个自己写的样例,才一举拿下这道题。所以测试真的很重要啊!