CCF CSP真题——201709-2 公共钥匙盒

来源:互联网 发布:个人简历模板软件 编辑:程序博客网 时间:2024/05/29 18:32

        本题为CCF CSP2017年9月题目的第二题。我第一次做这道题的时候,使用了结构体,以及许多vector,以至于逻辑分析的时候因为名字太多,自己都感觉很累。虽然最后成功做出,提交满分,但一直不是很满意。于是过了一阵子(也就是最近)又做了一遍,理顺了思路,终于把冗长的代码简化(80~90行→40行),所以现在想要来分享一下我做这题的思路,并给出提交得到满分的C++代码。题目不贴,我认为看到本文的应该已经提交过至少一次该题的程序了,所以我会直接分享我的思路,也因此不建议没做过本题的童鞋看本篇文章。

        碰到这样的模拟题,一般都要把题目读完,并且从中理顺逻辑,具体的流程应该是怎么样的。题目中使用&归还钥匙的要点如下:

        (1)取钥匙的时候,老师们都会找到自己所需要的钥匙将其取走,不会移动其他钥匙。这个非常好理解,keys[pos] = -1即可。

        (2)还钥匙的时候,还钥匙的老师会找到最左边的空的挂钩,将钥匙挂在这个挂钩上。这个也没问题,关键是找到位置。

        (3)如果有多位老师还钥匙,则他们按钥匙编号从小到大的顺序还。这意思就是,同一时刻可能有多个老师还钥匙,因此要维护一个表,存储当前时刻所归还的钥匙的编号,并且还要从小到大排序。因为钥匙肯定不会重复,所以使用C++标准库的set可以非常方便地实现这个需求。另外,这也提醒了我们,还钥匙可能多人同时还,那取也有可能多人同时取。但是取钥匙是按钥匙号取的,那就没set什么事了,乖乖用vector或数组,然后查钥匙号就行。

        (4)如果同一时刻既有老师还钥匙又有老师取钥匙,则老师们会先将钥匙全还回去再取出。In a word, 就是先还再取。

        大概理清了怎么操作之后,现在来看如何实现整个程序。一般这个流程都是“处理输入——实现主要操作——输出结果”

        首先是处理输入,这题的输入格式让我们产生一个很直观的想法,就是将每节课存在一个结构体中,包含使用的钥匙号,上课时间,下课时间(或者课程时长)。但问题是后来进行钥匙操作的时候,因为每个时刻可能存在多个归&取操作,所以即使根据这个结构体获取了上下课时间,也没有用,你还是要联系整体,看一下这个时刻有哪些钥匙需要操作。也就是说,这题应该按时间线做,得维护每个时刻的归&取表。那这样就很容易发现,之前声明的结构体毫无用处……

set<int> ret[10101]; // 归还数组,保存当前时刻应还的钥匙vector<int> take[10101]; // 保存当前时刻将要使用的钥匙

        所以重点是将时间与归&取表对应起来。我直接用代码的形式描述吧,简单地说就是类似“散列”的思想。

for (int i = 0; i != k; ++i){cin >> w >> s >> c;take[s].push_back(w); // s时刻将会使用wret[s + c].insert(w); // s+c时刻将归还w}

        接下来要进行归&取的模拟了,刚才提到了应该按时间线进行,所以就for 1 to 10101,然后先还再取就行了。我在输入数据时保存了最晚时间,所以我是for 1 to last。当然还有更“强迫症”的策略——就是维护一个set,保存“有操作”的时刻(时间线显然要从小到大,并且要去重,所以肯定用set)。每次输入的时候,把 s 和 s+c 一起insert到set中。这样就直接for (int i : set)即可。不过我的代码没有这样写……

        具体的归&取代码如下。注意这里的break一定要有,归&取当然只需要一次了。不然你可以试试去掉,运行一下看看结果。还有就是不要想当然地以为位置为k的地方存的是k,这个地方比较容易犯错。

for (it = ret[i].begin(); it != ret[i].end(); ++it)for (int j = 1; j != n + 1; ++j)if (keys[j] == -1){keys[j] = *it;break;}for (int k = 0; k != take[i].size(); ++k)for (int j = 1; j != n + 1; ++j)if (keys[j] == take[i][k]){keys[j] = -1;break;}

        最后就是输出结果了,直接输出 keys[] 即可。下面给出完整的代码。

#include <iostream>#include <vector>#include <set>using namespace std;set<int> ret[10101]; // 归还数组,保存当前时刻应还的钥匙vector<int> take[10101]; // 保存当前时刻将要使用的钥匙int main(){int n = 0, k = 0, w = 0, s = 0, c = 0, last = 0;cin >> n >> k;vector<int> keys(n + 1);for (int i = 1; i != n + 1; ++i)keys[i] = i;for (int i = 0; i != k; ++i){cin >> w >> s >> c;take[s].push_back(w); // s时刻将会使用wret[s + c].insert(w); // s+c时刻将归还wlast = (s + c) > last ? (s + c) : last; // 最终时间}set<int>::iterator it;for (int i = 0; i != last + 1; ++i){ // last之后没有数据了for (it = ret[i].begin(); it != ret[i].end(); ++it)for (int j = 1; j != n + 1; ++j)if (keys[j] == -1){keys[j] = *it;break;}for (int k = 0; k != take[i].size(); ++k)for (int j = 1; j != n + 1; ++j)if (keys[j] == take[i][k]){keys[j] = -1;break;}}for (int i = 1; i != n + 1; ++i)cout << keys[i] << " ";cout << endl;}

原创粉丝点击