总结
参加C站比赛的人越来越少了,大概也是因为题目水平一次次的刷新了质量下限吧。反馈的问题很少去解决,新的问题不断的出现。但凡正经的竞赛选手都不会靠这类竞赛来提高算法水平的,还在坚持的兄弟大概也只是为了书籍吧。经常参加的几个兄弟的题解都开启了付费阅读模式,像这种免费的题解也会越来越少了。
本次比赛选择题丢了10分,加上错误的 T 1 T1 T1,成绩并不理想。虽然读题时就知道有问题,但是放弃了在群里艾特主办方的想法,因为历史经验告诉我们,如果题目有问题,你反馈出题方大概率不会理你的;如果题目答案有问题,也只会将错就错,不会去重新评判的,所以久而久之就没人反馈bug了。奔着学习的目的阅读的可以只阅读下 T 2 T2 T2的题解。
题目列表
1.贝博士的论文审阅统计
题目描述
贝博士经常收到申请他审阅论文的信函,每封信函的信封上面只有两个申请人的编号,且每个申请人只能申请审阅一篇论 文,但可以重复申请。贝博士请艾小姐根据信封上的申请人编号统计下:一共有多少篇论文要审阅,最多申请人的论文有 多少人申请?
输入样例:
6 3
1 2
1 3
4 5
输出样例:
3 3
分析
本题的平均得分是0,题目不难,但是出题人的语文水平以及错误的用例让人想得分都难。建议出题人出题后找个审阅题目的,确保别人能读懂他的表达再将题目上线。题目阅读了几十遍,最后还是没有通过一个用例,下面用英语阅读理解的方式来解释下题目。
贝博士经常收到申请他审阅论文的信函
这句话应该是病句,大概意思是这个人经常收到信,这个信是让他帮忙审阅论文的。
每封信函的信封上面只有两个申请人的编号
每个信封上写了两个人的编号,是不是代表着一封信是用来申请一篇论文的审阅的,然后写着两个人的编号代表这篇论文是两个人共同申请的,题目没有说清楚。
且每个申请人只能申请审阅一篇论 文,但可以重复申请。
一种理解是每个人可以多次写申请信,但是贝博士只会给一个人审阅一篇论文,所以会忽略这个人其它的申请;但是可以重复申请加上用例的意思,另一种理解应该更为合理:每个人可以多次写申请信,但是申请的都是同一篇论文,比如用例的一封信上写着1 2,另一封信上写着1 3,都有1申请说明这两封信是申请同一篇论文的。
贝博士请艾小姐根据信封上的申请人编号统计下:一共有多少篇论文要审阅,最多申请人的论文有 多少人申请?
按照上面的分析,一封信代表申请的同一篇论文,两封信上编号相同代表申请的是同一篇论文(这两个关键信息题目都没有说明,需要推理出来),这样理解是可以求出一共有多少篇论文要审阅的。另一句话再次体现了出题人的语文功底了,最容易理解为“最多人申请的论文有多少人申请”,这样理解还是比较合理的,但是这句话写成这样,八成是另一种解释方式:“写了最多申请信的人申请的论文有多少人申请”,比如说1这个人先和2一起写了封申请信,后面又和3一起写了封申请信,由于3和6也一起申请过,说明1 2 3 6申请的是一篇论文,这样这篇论文就有4个人申请,但是这个输出的人数包不包含1自己呢?
题目想要表达的意思应该是想让我们求个并查集,所有共同写过申请信的人申请的都是一篇论文,最后输出并查集的个数以及并查集的大小。但是用例里1 2 3 6共同申请了一篇论文, 4 5一起申请了篇论文,应该输出2 4才对,用例给出3 3的输出只有两种解释:要么是满是病句和不充分信息的题目想要表达的是另一种意思,要么是出题人想出个并查集的题目,但是写的示例代码逻辑有问题,导致用例结果不对,后台测例答案也不对。没有用例解释,到底是什么情况我们也不得而知了。
比赛时写了并查集的代码,鉴于没有得分,就不贴出来了。
2.括号匹配
题目描述
有四种括号:大括号{}、中括号[]、小括号()和尖括号<>。它们之间不能错套,且需要配对。 错套的意思是:某种括号 的前半部分和后半部分之间,仅套着不同类括号的某一半部分而没有另一半部分。错套的样例如下: <<<>>{> ([hello[]]}){world} 因为在尖括号的前半部分和后半部分之间,仅套着大括号的前半部分而没有后半部分。 配对的意思 是:如果出现了某种括号的前半部分,则必须在之后有同样数量的同类括号的后半部分与之对应;反之如果出现了某种括 号的后半部分,则必须在之前有同样数量的同类括号的前半部分与之对应。正确的配对样例如下: <<<>>> ({[hello[]]}){world} 错误的配对样例如下: ))<<<>>([hello[]]){world} 因为前2小括号只有后半部分而缺失了前半 部分,而尖括号的前半部分有3个,而后半部分只有2个。
分析
又是一道较为复杂的模拟题,复杂的地方在于题目的理解,模拟题一般都是冗长的题目描述考察阅读理解,题目一般会给不足够准确的规则让你去理解,这点无可厚非,因为模拟题一旦条理清晰,每个规则都描述清楚就相当于送分题。
这题用例的解释还算明确,唯一不明确的是错误匹配的输出,说是按照大括号到小括号的顺序,左括号、右括号的顺序输出,那么是应该输出{[ }]的顺序还是{} []的顺序呢?用例来看是后一种,这题第一次提交通过了九成用例,后面改成后一种输出顺序就AC了,编码难度不大。
首先理解下题意:题目给了三种结果:
- 匹配:这个很好理解,类似于[()]这种正常的使用顺序。
- 错套:比如[),理论上出现[后应该出现],但是错误的出现了其它右括号。
- 错误配对:这个可以理解为缺失配对,比如[(有左括号没右括号,或者()]]有右括号没对应左括号。
括号匹配问题一般用栈来模拟,模拟的过程就比较考察编程功底了。遍历输入的括号序列,模拟过程如下:
-
遍历到的是左括号,直接入栈;
-
遍历到的是右括号:
首先看栈是否为空,空就说明缺失了配对,记录第三种情况缺失的符号。
栈非空就看栈顶的左括号与当前右括号是否匹配,匹配栈顶元素就可以出栈,记录下匹配的括号数。
栈顶左括号与当前右括号不匹配,说明存在错套,直接输出返回即可。
-
遍历结束,如果栈非空,说明还存在缺失匹配的左括号,需要记录下。
具体的实现细节可以参考代码,添加了详细的注释。
代码
#include <iostream>
#include <map>
#include <string>
#include <stack>
using namespace std;
/*
错套:右括号出现前套着不同括号的左括号但是没有右括号,和栈顶不匹配
错误配对:出现右括号没有左括号,出现左括号没有右括号
*/
stack<char> l;//存储左括号的栈
map<char,char> m;//key-左括号,value-右括号
map<char,int> cnt;//正常匹配的左括号数及数量
map<char, int> mis;//缺失匹配的多余括号及数量
int main() {
string s;
getline(cin, s);
char left[4] = {'{', '[', '(', '<'};
char right[4] = {'}', ']', ')', '>'};
for(int i = 0; i < 4; i++) m[left[i]] = right[i];
int n = s.size();
bool flag = false;//是否存在错误匹配
for(int i = 0; i < n; i++) {
if (s[i] == '{' || s[i] == '[' || s[i] == '(' || s[i] == '<') {
l.push(s[i]);
} else if(s[i] == '}' || s[i] == ']' || s[i] == ')' || s[i] == '>') {
if (!l.size()) {//error match
mis[s[i]]++;
flag = true;
} else {
char t = l.top();
if (m[t] == s[i]) {//match
cnt[t]++;
l.pop();
} else {
cout<<t<<s[i]<<"!"<<endl;
return 0;
}
}
}
}
while (l.size()) {//栈非空存在缺失匹配
mis[l.top()]++;
l.pop();
flag = true;
}
if (flag) {
for (int i = 0; i < 4; i++) {
if (mis[right[i]]) cout<<mis[right[i]]<<left[i];
if (mis[left[i]]) cout<<mis[left[i]]<<right[i];
}
} else {
for(int i = 0; i < 4; i++) {
if(cnt[left[i]]) cout<<cnt[left[i]]<<left[i]<<right[i];
}
}
cout<<endl;
return 0;
}