回顾:问题A
一栋10层的大楼(楼层编号1-10),设有一台无限载重的电梯,初始时电梯停在1层。电梯移动1层的耗时为1,在某一层停靠的耗时为1(时间初始为0)。为了使得乘客等待的时间(电梯在目的层的停靠时刻 - 乘客发出请求时刻)总和最小,请你编写一个程序来进行电梯调度。
输入有5个请求,每个请求一行,格式为请求时刻 起始楼层数 去往方向,其中方向为0代表向上去往10层,为1代表向下去往1层。
输出每次对应的决策,每一行的输出格式为xx时,停靠在x楼。其中,“xx时刻”指的是在某层楼停靠的时刻,且不算入在该层的停靠时间。如:
当0时刻时,电梯此时在1层,输入有0 1 0,那么电梯从1层接客(1s)前往10层(9s),应输出10时,停靠在10楼(1+9=10)。此时,该乘客等待时间为(10-0=)10。
当0时刻,电梯此时在1层,输入有0 2 0,那么电梯从1层前往2层(1s),接上乘客(1s),前往10层(8s),应输出10时,停靠在10楼(1+1+8=10)。此时,该乘客等待时间为(10-0=)10s。
最后输出完成5个请求(所有乘客都到达目的地)后,各乘客的等待时间总和。
请自己设计5组测试用例,且具有一定代表性,用以验证程序是否是最小耗时。
问题B
要求:
一、 输入从文件读取,输出是写入文件。
大家在学习 C 语言的时候,应该明白了控制台程序的输入和输出。但文件操作往往是之前的课程中被忽略的一个部分,大家有没有试过“如果我要将程序的输出保存下来”、“如果我要从一个文件中获得输入”这些实操的环节呢?如果没有,现在是一个再好不过的学习机会,来吧,learning by doing!
要求:输入输出改成文件输入输出操作。输入文件的文件名为 input.txt 里面保存着输入样例,一个输入为一行,输入的格式见作业的第二条要求。
例如:
0 1 5 2 2 5
程序输出每次对应的决策,每一行的输出格式为 xx时,停靠在x楼。输出在文件名为output.txt的文件中。
二、这次的乘客不再像上一次作业那样只会去顶楼或者是 1 楼了,现在要使我们的程序功能更加贴近现实。
迭代:乘客会前往任意一层,请修改程序代码并实现。自然,输入请求就变为了:每个请求一行,格式为 请求时刻 起始楼层数 去往楼层 。同样,大家也需要自己构造测试样例,让这台“电梯”尽可能好的工作。构造五个输入样例,最后输出完成五个请求(所有乘客都到达目的地)后,各乘客的等待时间总和。输入输出的详细例子可以参加上一次作业的要求。
思考过程(问题B)
提示:本文针对的是我对这题的思考过程,思路不止一种。
这题和上周的题相比,不过是多了一个新的参数:到达楼层,也就是上一个问题的策略(遍历乘客请求)中遍历乘客请求的等待楼层的时候还要额外判断乘客状态并且重新遍历乘客到达楼层,如此遍历反而显得不直观而且还有重复计算增加时间复杂的风险(况且上一个程序的全排列效率是真的低迷),所以还是牺牲了空间新增了一个新的数组来直接存储需要停靠的楼层,这样只需要遍历数组即可,结构也从全排列改为递归(10!的大小真的不敢想象)。
这么改了之后效率果然上来了(最明显的表现就是出现死循环的时候系统时间参数(测试输出)一下就飙到了100w(一脸疲惫.jpg)),也算是实现了上一个问题思考结尾留下的改进方法。
还有一个文件输入输出的要求,作为混迹多年的NOIP鱼塘选手,不假思索的就打出来了。
#include<cstdio>
int main()
{
freopen("input.txt", "r", stdin);//第一个参数填文件路径,第二个参数填打开类型,第三个参数为标准输入/输出流
freopen("output.txt", "w", stdout);//如果第一个参数所打开的文件不在连接后的exe的目录,需要填写文件完整路径,参数为"w"时若无该文件则在目标路径创建
...
fclose("input.txt");//关闭文件
fclose("output.txt");//""内的内容同freopen的第一个参数
}
/*
打开类型:
"r":读入
"w":写入
*/
不过百度了一下,貌似C++有更高效的文件流输入输出(fstream)。
#include<fstream.h>
int main()
{
istream input("input.txt");//文件流输入,input为文件名,如果所打开的文件不在连接后的exe的目录,需要填写文件完整路径
ostream output("output.txt");//文件流输出,如果所指向路径不存在该文件则在目标路径创建
int a;
input >> a;
...
output << a;
input.close();//关闭文件
output.close();
}
但是总觉得没有freopen灵活,所以我的程序暂时没有用到C++的文件流输入输出。
代码概览
代码名 | 长度 | bug数量 | 花费时间 |
---|---|---|---|
B.cpp | 159行 | 4个 | 大约6h |
执行数据
注:部分执行数据参考@Stolf。
先测试一些比较常规的数据
1、同时在相同楼层发出指令,前往同一楼层。
2、同时在相同楼层发出指令,前往不同楼层。
3、同时在不同的楼层发出指令,前往相同楼层。
4、同时在不同楼层发出指令,前往不同楼层。
四个“同时”的意义是把电梯看成会“未卜先知”,先确定电梯在基本层面没有毛病(其实也挺复杂的)。
然后才是比较复杂的数据
1、会返回的数据
2、不能返回的数据
3、还有这个被我笑了很久的来自Stolf的“复杂”数据
和@Stolf和@Seast互拍了答案之后,运行结果都基本一样,应该是没有太大的问题了。其实因为是爆搜算法,所以只要能正常运行基本上就已经可靠了,这些数据更多的是用来排bug和让人更加信服。
收获
这一次的作业和上一次相比,只是略微的改动,是可以把上个问题的A.cpp拿来直接改的,但是由于不成熟的编码规范和注释习惯,使得修改代码成为了本次作业的难题,甚至萌生了多次想要直接推掉重写的冲动。今后写的程序肯定要比这大的多,而重复修改肯定也是必不可少的,因此“因算法竞赛留下的恶习”形成的编码规范必须要得到重视,今后要不断的努力改正这个毛病。
GitHub使用心得
不得不说GitHub真是相当的有趣,以前就已经听闻其鼎鼎大名,如今真正的注册并使用后才知道代码开源社区的奇妙。
而GitHub代码管理的基础git更是奥妙重重,特别是规范使用git commit,是开源社区代码版本规范管理的重点。第一次上手的时候并不知道commit信息是可以删除的,而当时写的README文件又遇到了很奇怪的问题,于是出现了下面一副喜闻乐见的情况:
刚好自己近期开始接触Linux系统,终端的灵活使用显得相当重要,而寒假的一系列作业和git刚好能锻炼我使用终端,一举两得,也算是幸运)。