CSDN编程竞赛报名地址:https://edu.csdn.net/contest/detail/16
(请不要删掉此地址)
“路漫漫其修远兮,吾将上下而求索”
前言
笔者属于刚入门算法的萌新,也有参加天梯赛,蓝桥杯,acwing等这些算法比赛,成绩算一般,但是还在努力学习。
本次是笔者第一次参加CSDN的编程竞赛,因为是第一次参加,不太熟悉答题的流程,导致成绩并不是很高,但是笔者认为此次的四道题均不是很难,所以想借着创作活动来分享一下我的答题思路。
大赛简介
活动时间:9月8日-21日(竞赛时间截止9.18)
竞赛考试时间:9月18日 8:30-11:00(作答时间2小时)
获奖名单公布:9月23日,在本页面公布获奖名单链接
获奖用户信息收集:9月27日
奖品发放:9月30日后7个工作日内
竞赛:满分100分,4道编程题,编程题可使用Java、C、C++、C#、Python、JavaScript、lua、go等编程语言
CSDN编程竞赛是CSDN举办的编程比赛,一共四道编程题,难度适中,对萌新友好
参赛流程
因为是第一次参赛,比较匆忙,早上起床洗漱完就赶紧坐到电脑旁边,打开比赛网址进行答题,所以笔者建议大家早些起床,提前准备比赛的环境。
其次是比赛过程中要细心,笔者这次因为不细心导致出现了小的问题,也是比较后悔,但是后面重新去完成了错误的题,也算是对题目的再次熟悉。
参赛经历
提示:可介绍自己是如何准备参赛的,以及比赛过程中的分阶段时间安排和分工。
笔者参加的是第六期的比赛,因为四道编程题的难度是从简到难,所以按顺序从第一题开始做即可,如果遇到调试有问题,可以进入自测界面,通过设置输入样例,预期结果,即可分析实际输出的情况。
解题思路
1.题目名称:严查枪火
X国最近开始严管枪火。 像是“ak”,“m4a1”,“skr”。都是明令禁止的。 现在小Q查获了一批违禁物品其中部分是枪支。小Q想知道自己需要按照私藏枪火来关押多少人。 (只有以上三种枪被视为违法)
样例输入
5
ak
m4a1
skr
uzi
98k
样例输出
3
分析
本题应该算是送分题,只需要判断输入的是不是“ak”,“m4a1”,“skr”,然后统计即可,下面直接上代码。
import java.util.*;
public class Main {
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
String str_0 = scan.nextLine().trim();
int n = Integer.parseInt(str_0);
ArrayList<String> vector = new ArrayList<>();
for (int i = 0; i < n; i++) {
String str_1 = scan.nextLine().trim();
vector.add(str_1);
}
scan.close();
int result = solution(n, vector);
System.out.println(result);
}
public static int solution(int n, ArrayList<String> vector) {
int result = 0;
for (int i = 0; i < n; i++) {
switch (vector.get(i)) {
case "ak":
case "skr":
case "m4a1":
result++;
break;
}
}
return result;
}
}
2.题目名称:鬼画符门
鬼画符门,每年都会统计自己宗门鬼画符消耗的数量,往年一直是大师兄管理, 但是这次鬼艺接手了, 你能帮鬼艺写一个程序统计每年消耗数量最多的鬼画符吗?
样例输入
5
red
red
green
green
blue
样例输出
red
分析
本题分析其实就是求出现次数最多的字符串,也很简单,直接上代码。
import java.util.*;
public class Main {
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
String str_0 = scan.nextLine().trim();
int n = Integer.parseInt(str_0);
ArrayList<String> vector = new ArrayList<>();
for (int i = 0; i < n; i++) {
String str_1 = scan.nextLine().trim();
vector.add(str_1);
}
scan.close();
String result = solution(n, vector);
System.out.println(result);
}
public static String solution(int n, ArrayList<String> vector) {
String result = "";
//创建一个HashMap存每种颜色的数量
HashMap<String, Integer> maps = new HashMap<>();
for (int i = 0; i < n; i++) {
String str = vector.get(i);
//判断maps里面有没有该颜色
if (maps.containsValue(str)) {
//有的话就加1
int num = maps.get(str);
maps.put(str, num + 1);
} else {
//没有的话就置1
maps.put(str, 1);
}
}
int max = -1;
for (String str : maps.keySet()) {
if (maps.get(str) > max) {
max = maps.get(str);
result = str;
}
}
return result;
}
}
3.题目名称:收件邮箱
已知字符串str,str表示邮箱的不标准格式。 其中”.”会被记录成”dot”,”@”记录成”at”。 写一个程序将str转化成可用的邮箱格式。(可用格式中字符串中除了开头结尾所有”dot”,都会被转换,”at”只会被转化一次,开头结尾的不转化)
样例输入
kevinwalkeratcsdndotcom
样例输出
kevinwalker@csdn.com
分析
本题因为笔者的不信息,导致出现问题,后面重新分析,完成了正确的结果。
本体主要条件为除了开头结尾所有”dot”,都会被转换,”at”只会被转化一次
所以我们只需要不让开头结尾的dot转换,并且转换一次at即可
因为字符串处理方法很多,笔者的思路是:
先将所有的dot
转换为.
,下面再判断首位是否为.
,如果为.
就把点再换成dot,然后再通过reverse()
方法逆序字符串,再照上面的思路继续转换
最后再通过replaceFirst()
方法把at
转换为@
即可,下面上代码。
import java.util.*;
public class Main {
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
String str_0 = scan.nextLine().trim();
String str = str_0;
scan.close();
String result = solution(str);
System.out.println(result);
}
public static String solution(String str) {
String result = str;
//把dot全部替换为.
result = result.replaceAll("dot", ".");
//判断首位是否为.
if (result.indexOf(".") == 0) {
result = result.replaceFirst(".", "dot");
}
//判断末尾是否为.
//逆序字符串
result = reverseStr(result);
if (result.indexOf(".") == 0) {
//替换逆序的首位
result = result.replaceFirst(".", "tod");
}
//字符串再逆序回来
result = reverseStr(result);
//转换一次@
result = result.replaceFirst("at", "@");
return result;
}
public static String reverseStr(String str) {
StringBuilder sb = new StringBuilder(str);
return sb.reverse().toString();
}
}
4.题目名称:最长递增的区间长度
给一个无序数组,求最长递增的区间长度。如:[5,2,3,8,1,9] 最长区间 2,3,8 长度为 3
样例输入
6
5 2 3 8 1 9
样例输出
3
分析
第一眼看到这个题,笔者想到是LIS(最长上升子序列)
,但是根据题目要求,只需要求最长连续递增的子序列长度
,那么分析就很简单了,直接讲思路:
首先我们写一个List用来保存每个数字的连续子序列长度,然后一次判断前一位是否小于自己,小于则+1,大于则重置为 1。
直接上代码:
import java.util.*;
public class Main {
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
String str_0 = scan.nextLine().trim();
int n = Integer.parseInt(str_0);
String str_1 = scan.nextLine();
String[] line_list_1 = str_1.trim().split(" ");
ArrayList<Integer> arr = new ArrayList<>();
for (int i = 0; i < line_list_1.length; i++) {
arr.add(Integer.parseInt(line_list_1[i]));
}
scan.close();
int result = solution(n, arr);
System.out.println(result);
}
public static int solution(int n, ArrayList<Integer> arr) {
int result = 0;
List<Integer> list = new ArrayList<>();
list.add(1);
for (int i = 1; i < n; i++) {
if (arr.get(i) > arr.get(i - 1)) {
list.add(list.get(i - 1) + 1);
} else {
list.add(1);
}
}
result = Collections.max(list);
return result;
}
}
5.笔者补充的题目:最长上升子序列
给一个无序数组,求最长递增的区间长度。如:[5,2,3,8,1,9] 最长区间 2,3,8,9 长度为 4
样例输入
6
5 2 3 8 1 9
样例输出
4
分析
这个题相较于本次比赛的第四题是有些相似的,但是去掉了连续
的条件,所以我们要用动态规划来分析。
首先列出dp数组的定义:a[i]表示arr[i]为结尾的最长子序列长度
状态转移方程:a[i] = a[j] + 1 条件为:( 0 < j < i , arr[j] < arr[i] )
众所周知,动态规划就是空间换时间
,所以我们要通过建立数组a[]
来保存状态
a[]
的索引即是所给数组每一位的情况下的最长子序列长度。
每次给a[i]
初值置1,然后一次与i
前面的j
位进行比较,然后取符合arr[j] < arr[i]
的情况下的max(a[j]+1,a[i])
举个例子:
假设当前数组为[5,2,3,8,1,9]
第一个数 5,最长上升子序列为[5],长度为1
第二个数 2,在2前面未找到比2小的数字,所以最长上升子序列为[2],长度为1
第三个数 3,在3前面找到比3小的数字2,所以max(1,1+1),所以最长上升子序列为[2,3],长度为2(1+1是指上一项2的最长上子序列长度)
第四个数 8,再8前面找到了3个比8小的数字,分别用他们的长度+1与8的初始长度比较(长度初始值为1)
,所以最长上升子序列为[2,3,8],长度为3
第五个数 1,在1前面未找到比1小的数字,所以最长上升子序列为[1],长度为1
第六个数 9,再9前面找到了5个比9小的数字,分别用他们的长度+1与9的初始长度比较(长度初始值为1)
,所以最长上升子序列为[2,3,8,9],长度为4
如果你有大概的想法,但是脑子里并没有大概的代码,直接看下面的代码就可以明白了:
import java.util.*;
public class Main {
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
String str_0 = scan.nextLine().trim();
int n = Integer.parseInt(str_0);
String str_1 = scan.nextLine();
String[] line_list_1 = str_1.trim().split(" ");
ArrayList<Integer> arr = new ArrayList<>();
for (int i = 0; i < line_list_1.length; i++) {
arr.add(Integer.parseInt(line_list_1[i]));
}
scan.close();
int result = solution(arr);
System.out.println(result);
}
public static int solution(ArrayList<Integer> arr) {
int result = 0;
if(arr.size() == 0) return 0;
int[] a = new int[arr.size()];
int res = 0;
for(int i = 0; i < arr.size(); i++){
a[i] = 1;
for(int j = 0; j < i; j++){
if(arr.get(j) < arr.get(i)){
a[i] = Math.max(a[i], a[j]+1);
}
}
result = Math.max(result, a[i]);
}
return result;
}
}
经验心得
提示:可总结自己参赛的经验心得,给大家一些建议参考。
本次参加完比赛的最大心得就是一定要好好准备,不要像笔者一样粗心大意,导致犯了很多不该犯的错误
总结下来就是:本次比赛的题难度都不大,如果你是跟笔者一样刚入门算法的小白的话,可以当作锻炼的过程来参加比赛,会对你有很大的帮助,而且最好是可以在参加完比赛后,将错的地方重新写一遍,把粗心的地方细心总结一下,如果可以温故知新那就是最好,这也是笔者在最后面补充了一道最长上升子序列的问题一样。