前言

针对没有任何编程经验的同学写一份C++的教程是十分困难的,因为C++光是语法部分就能填充一本如同字典一般厚的书,而在算法竞赛中,我们仅仅是选择学习一些需要用到的,足以完成问题求解即可

学习程序语言和学习语言的过程是类似的,可以通过反复地模仿来熟悉,不断地尝试来积累,然后慢慢地能够做到自我创造,所以呢,不要踌躇不前,不要觉得难以下手,去模仿,去写去尝试

本教程基于USACO Training,结合题目内容讲解,随缘更新,如果对零基础的同学能够起到一些帮助,那就再好不过了

由于知识的诅咒,我可能已经很难明白初学者可能会遇到什么问题了,如果感觉写的某些东西晦涩难懂,欢迎与我交流,但是最好,你可以善用搜索引擎和手头的工具书来自行找到解决问题的方法

题意

[Your Ride Is Here]

将两个字符串按照规则转化成数字,判断是否相等

规则:A-Z对应1-26,将串中每个字母转化成数字,相乘后对47取模

INPUT
1
2
COMETQ
HVNGAT
OUTPUT
1
GO

开始写代码

首先写下头文件和命名空间

1
2
#include <bits/stdc++.h>
using namespace std

这是在算法竞赛中非常常用的一个头文件,里面包含了相当多的库函数,std命名空间是C++标准为了不与原来的头文件(C)混淆而建立的,当然你也可以建立自己的命名空间,引入std是为了std名字空间的东西全部暴露到全局域中

比如原来想使用cout,要写成std::cout

在暴露到全局域中之后直接写cout就可以了

数组与变量

在程序中变量的作用就是承载值,比如你需要保存一个值,读入一个值,或是对某个值做反复的计算,那就需要用到变量了

1
2
int x = 3;
int y = (x + 5) * 2;

比如上述代码就是定义了一个变量x为3,然后用x去计算变量y,y用于承载式子的计算结果

int是数值型变量,字符型变量则需要用char,更多的类型大家可以在使用中自行查阅资料

而数组呢,就是变量的一个有序集合,比如说,我想要计算一个数列,并且保存下每个数列中的元素,那我就需要使用数组了

1
int a[10];

上述代码定义了一个长度为10的数组a,在c++中,数组是0-base的,也就是说定义的a[10],包含了0到9为下标的10个变量,我们只要在方括号里写入下标,就可以使用对应的变量

比如:

1
2
3
a[1] = 5;
a[2] = 4;
a[3] = a[1] * 3 + a[2] * a[2];

常量

在题目中通常会有一些固定的值,比如这道题中的模数47,我们通常用const常量来表示这些值,使得自己的代码更具有可读性,也防止了常量变大(比如999999997)导致的在反复使用中产生的抄写错误

题目中用到的数组,一般也是用略大于数值上限的常量来定义大小

而这道题中我们需要用到的就是数组的长度L和模数mod

1
2
const int L = 6 + 10;
const int mod = 47;

对于题目描述的两个字符串,我们可以用字符数组来定义

1
char team[L], comet[L];

循环

循环可以看成是相似行为的有序处理队列,比方说,你想要计算一个数列$a_n=a_{n-1}+a_{n-2}$,难道说得一个一个来计算么?

1
2
3
4
5
a[1] = a[2] = 1;
a[3] = a[2] + a[1];
a[4] = a[3] + a[2];
a[5] = a[4] + a[3];
……

求a[100],a[1000]简直不敢想

循环呢,就是通过一个下标变量的变化来表示重复过程的计算

比如要计算a[100],可以这么实现

1
2
3
a[1] = a[2] = 1;
for (int i = 3; i <= 100; i++)
a[i] = a[i-1] + a[i-2];

for是循环的一种实现方式,更多的循环方式比如while,do-while大家可以自行去了解

for循环用分号隔开的三个位置起到的是不同的作用,均可以为空,第一个位置在循环的开始前执行,可以定义,也可以计算,第二个位置在循环的每轮计算进入前进行判断,满足条件则进入循环,否则结束循环,而第三个位置,则在循环的每轮计算结束后执行

当然循环也可以强制结束,比如我算到大于100的数列值就不继续算了,那就用break语句来结束循环(示例下面会有)

条件判断

条件判断其实很好解释,就是如果(if),如果今天下雨,那么我要带上雨伞,否则,不带,那么程序如何实现这样的逻辑呢,答案就是条件判断

那么来看一下如何实现之前提到的,算到大于100的数列值就不继续算的功能

1
2
3
4
5
a[1] = a[2] = 1;
for (int i = 3; i <= 100; i++) {
a[i] = a[i-1] + a[i-2];
if (a[i] > 100) break;
}

首先代码中我用大括号将两句话括在了一起,这样的操作在C++中表示它们同属于这个循环(或者说属于某个过程),如果不加大括号,那么循环就只能执行一句话

那如何用条件判断来处理比较复杂的分类呢,比如60分及以上且75以下给及格,75及以上且85以下给良,85及以上给优,那就要用if else了

1
2
3
4
5
6
7
8
9
10
11
12
if(x < 60){
给不及格
}
else if(x < 75){
给及格
}
else if(x < 85){
给良
}
else{
给优
}

else表示将之前的判断排除在外的剩余部分,可以嵌套

好了,我们现在有了以上的工具,就可以来看看能在这道题目中做些什么事情

第一步,要将字符串计算为对应的数字

1
2
3
int sLen = strlen(s), sVal = 1;
for (int i = 0; i < sLen; i++)
sVal = sVal * (s[i] - 'A' + 1) % mod;

首先我们用字符数组的strlen函数(库函数)来计算其长度sLen,定义一个初始变量sVal来表示最后计算的结果,然后用一个循环来遍历字符串中所有的字母,计算其对应的数字并乘到sVal上

我们对两个字符串分别算出对应的值,比较结果就可以完成题目的要求

函数

最后我们来讲一下函数,这是程序设计中一个非常重要的内容,函数的目的是,让程序不重复做同一件事,让程序更模块化,同时也使得程序能够递归(之后遇到再讲)

比如说,对于两个字符串,转化为值,我们做的操作其实是一样的,只是换了变量名而已,所以我们就可以将这个过程定义为一个函数

格式如下

1
2
3
4
5
6
int cal(char s[]) {
int sLen = strlen(s), sVal = 1;
for (int i = 0; i < sLen; i++)
sVal = sVal * (s[i] - 'A' + 1) % mod;
return sVal;
}

函数的本质是将输入映射到输出,这里我们希望输入一个字符串,输出其对应的值,可以看出这个函数返回值是int,写在cal前面,如果函数不需要返回值,只是希望对某些变量做一些事情,则可以将返回值设为空void

在C++中,我们首先需要一个主函数main,表示程序的入口,除此之外可以自己建立函数来合并重复功能或者使得代码更有逻辑性

最终代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include <bits/stdc++.h>
using namespace std;

// 数组的长度设置最好略大于上限
const int L = 6 + 10;
const int mod = 47;

char team[L], comet[L];

// 计算字符串对应的数字
int cal(char s[]) {
int sLen = strlen(s), sVal = 1;
// 循环
for (int i = 0; i < sLen; i++)
sVal = sVal * (s[i] - 'A' + 1) % mod;
// 函数返回值
return sVal;
}

// 主函数
int main() {
// 字符数组读入
scanf("%s%s", team, comet);
// 三目表达式,自行查阅资料
puts(cal(team) == cal(comet) ? "GO" : "STAY");
return 0;
}

Accept

Q & A

Q1:好像很多内容没写清楚,太过简单了

A1:确实是这样,面面俱到的工程量太大了,入门的语法光看这篇文章一定是不够的,除了辅以语法书之外,可能还要多去完成类似的题目,多练多查才会快速熟悉起来,我只是扔了一块砖,希望你们可以自己去发现玉石

Q2:这题写的过复杂了,直接在主函数里计算,判断一下输出似乎就可以完成了

A2:主要是想强调一下函数这个内容,对于重复要做的事情,将其函数化,可能在短的程序里面体现不出什么优势,但是当要实现的内容增多,且存在大量复用时,这样的习惯会让你写出易读易调试的代码