CSDN编程竞赛报名地址:https://edu.csdn.net/contest/detail/16
(请不要删掉此地址)
总结
这次竞赛题目比较简单,没多大必要写题解。更多的还是给出自己的一些体会和建议吧。
很多同学已经对比赛规则和编程体验给出了建议,比如复制粘贴自己在当前页面写的代码也计算粘贴次数很不合理,遇见for循环或者名称较长的变量不能复制只能手敲体验不太好;题目给出了标准输入的语句,但是给出的solution接口远不如leetcode的接口友好,比如答案需要用long long表示,给出的模板函数返回值还是int,容易给人误导。再比如调试需要完成等待几秒才能再次调试或者提交,以及调试过程中的warning信息会参与结果的比对等等。我主要的建议还是在命题方面。
作为大规模的竞赛,这次竞赛的命题真的很不用心。竞赛一般都是出的新题,而这次比赛直接从之前csdn的每日一题中随机抽了几题,而且难度也没有控制好,题目过于简单没有区分度。而且题目的描述不够详细,用例不够详细。
我的建议是:找个有算法竞赛背景命题人出新题,而不是随便在题库里抽题,才能保证公平性。命题完成需要另外的人进行审核,题目语义是否准确,用例是否充分,难度设置是否合理。都是需要review的地方,不是一两句话概括题意,用例随便出个简单的,连基本输入格式都没有覆盖到,每道题至少出三个用例,覆盖不同的情况。下面结合具体题目来说下具体有哪些需要改进的地方吧。
题目列表
1.严查枪火
题目描述
X国最近开始严管枪火。 像是“ak”,“m4a1”,“skr”。都是明令禁止的。 现在小Q查获了一批违禁物品其中部分是枪支。
小Q想知道自己需要按照私藏枪火来关押多少人。 (只有以上三种枪被视为违法)
分析
作为简单的模拟题,如果给出了清晰的接口,是可以在两分钟内ac本题的,但是比赛时WA了几次才通过这题。因为题目没有对输入格式清晰地描述出来,这也与一开始我选择删掉题目的输入模板自己写输入有关,如果选择自己写输入的同学,就会发现题目对输入的描述有多么模糊了。
题目首先给定一个输入n,接下来输入n行字符串表示每个人携带的物品,问最后要关押多少人。题目唯一给的用例是一行一个字符串的情况,那么对于题意就有几种理解了:
- 1.输入n个字符串,判断这n个字符串中包含违禁物品的有几个。
- 2.输入n行字符串,每行字符串是连续的,通过对每行字符串的子串是否包含违禁品来判断是否应该关押。
- 3.输入n行字符串,每行字符串由多个按空格隔开的字符串表示,表示每个人携带的若干件物品的名字,只要携带了违禁品就关押。
显然,根据题意是无法判断题目输入到底是什么格式的,只能不断的提交,一种情况一种情况的尝试,直至通过本题。而最终题目后台的输入格式是第三种情况,我觉得要么题目描述清楚,每行表示每个人携带的若干件物品,输入用空格隔开;要么增加几个用例来体现输入的格式,而不是这样模糊的描述。
最后,自己写输入要注意的是,读取n后需要getchar()来去掉n后面的回车,否则getline读取的第一个字符串就是回车字符。
代码
#include <iostream>
#include <string>
#include <sstream>
#include <vector>
using namespace std;
string t[3] = {"ak", "m4a1", "skr"};
bool check(string &s) {
for(int i = 0;i < 3;i++) {
if(s == t[i]) {
return true;
}
}
return false;
}
int main() {
int n;
std::vector<std::vector<std::string>> vec;
std::cin>>n;
string s;
int res = 0;
getchar();
for(int i = 0;i < n;i++) {
getline(cin,s);
stringstream ss(s);
string x;
while(ss>>x) {
if(check(x)) {
res++;
break;
}
}
}
std::cout<<res<<std::endl;
return 0;
}
2.鬼画符门
题目描述
鬼画符门,每年都会统计自己宗门鬼画符消耗的数量,往年一直是大师兄管理, 但是这次鬼艺接手了, 你能帮鬼艺写一个
程序统计每年消耗数量最多的鬼画符吗?
分析
本题考查hash的简单应用。第一遍遍历字符串数组统计出每个字符串出现的次数,同时更新下出现最多的鬼画符次数。第二次遍历字符串数组,一旦遍历到某个鬼画符出现的次数等于最多的次数,返回该字符串即可。
代码
#include <iostream>
#include <string>
#include <sstream>
#include <unordered_map>
#include <algorithm>
#include <vector>
using namespace std;
unordered_map<string,int> m;
int main() {
int n;
std::cin>>n;
vector<string> t;
string s,res;
for(int i = 0;i < n;i++) {
cin>>s;
t.push_back(s);
}
int p = 0;
for(auto x : t) {
m[x]++;
p = max(p, m[x]);
}
for(auto x : t) {
if(m[x] == p) {
res = x;
break;
}
}
cout<<res<<endl;
return 0;
}
3.收件邮箱
题目描述
已知字符串str,str表示邮箱的不标准格式。 其中”.”会被记录成”dot”,”@”记录成”at”。 写一个程序将str转化成可用
的邮箱格式。(可用格式中字符串中除了开头结尾所有”dot”,都会被转换,”at”只会被转化一次,开头结尾的不转化)
分析
这题也是之前每日一题里面的题目,稍微需要多一点判断的模拟题,考察对字符串的处理。
可以用res来记录最终的结果。逐位遍历str,然后进行判断:
- 如果str[i]是‘d’,如果i不是在开头,也不是在str的倒数第三位,并且后两位是‘o’和’t‘,那么res需要追加上’.'。
- 如果str[i]是’a’,还是要判断是否在开头或者结尾,不在的话后面的字符是’t’,并且之前没有转化过才给res追加上’@'。
- 如果str[i]不是‘a’或者’d’,或者是前两种分支的else情况,直接追加到res末尾即可。
本题对字符串的模拟并不复杂,主要需要细心的地方是判断后面的字符时需要注意下标不要越界,以及判断为dot或者at后需要跳几位再继续遍历,最后就是在考虑到at只转化一次的基础上注意开头的at不转化的规则。
代码
#include <iostream>
#include <string>
#include <sstream>
#include <vector>
using namespace std;
std::string solution(std::string str){
std::string res;
int n = str.size();
bool flag = true;
for(int i = 0;i < n;i++) {
if(str[i] == 'd') {
if(i && i < n - 3 && str[i+1]=='o' && str[i+2]=='t') {
res += ".";
i += 2;
}
else res += "d";
}
else if(str[i] == 'a' && flag) {
if(i && i < n - 2 && str[i+1] == 't') {
res += "@";
flag = false;
i++;
}
else res += "a";
}
else res += str[i];
}
return res;
}
int main() {
std::string str;
std::cin>>str;
std::string result = solution(str);
std::cout<<result<<std::endl;
return 0;
}
4.最长递增的区间长度
题目描述
给一个无序数组,求最长递增的区间长度。如:[5,2,3,8,1,9] 最长区间 2,3,8 长度为 3
分析
这题也是每日一题的题目,第一次看见这题还以为用例有问题,因为第一印象是求最长递增子序列,然后觉得用例的最初递增子序列是2,3,8,9,长度是4。写了个LIS的代码提交上去WA了。再次读题发现求的是最长连续递增子序列。
一般算法题里不连续的叫做子序列,连续的叫做子串或者连续子序列,看见区间就没有反应过来。既然是求最长连续子序列长度,那么解法就比LIS问题更简单了。不用去考虑双指针或者DP的思想,直接模拟解决即可。
设res为当前遍历过的序列的最长连续递增子序列的长度,cnt是当前遍历到的最长连续递增子序列的长度,或者说是以当前遍历到的元素为末尾的最长连续递增子序列的长度。具体遍历过程就是:遍历当前元素,如果当前元素大于前一个元素,cnt++,否则cnt置为1表示以当前元素结尾的最长连续递增子序列的长度是1。每次cnt置1的时候以及遍历结束的时候尝试更新下最后的结果res即可。
遍历过程中蕴含DP的思想,即f[i]表示以第i个元素结尾的最长连续递增子序列的长度,最终的答案res = max(f[i])。由于在遍历过程中就更新了res,所以dp数组简化为了变量cnt。当然,写代码时候不用考虑背后的DP思想,也是可以很快写出来的。
代码
#include <iostream>
#include <algorithm>
#include <string>
#include <sstream>
#include <vector>
using namespace std;
const int N = 10005;
int f[N];
int solution(int n, std::vector<int>& vec){
int res = 1;
int cnt = 1;
for(int i = 0;i < n;i++) {
if(i && vec[i] > vec[i - 1]) cnt++;
else {
res = max(res,cnt);
cnt = 1;
}
}
res = max(res,cnt);
return res;
}
int main() {
int n;
std::vector<int> vec;
std::cin>>n;
std::string line_0, token_0;
getline(std::cin >> std::ws,line_0);
std::stringstream tokens_0(line_0);
while(std::getline(tokens_0, token_0, ' ')){
vec.push_back(std::stoi(token_0));
}
int result = solution(n,vec);
std::cout<<result<<std::endl;
return 0;
}