c++ 小教程
前言
- 由于其教程作者经历了很久的时间,因此码风可能有较大的差别。
基础语法
Hello world
在配完一个新环境的时候,为了检测配置有没有问题,我们一般都会以“输出Hello world”来检测。现在,我们就用 c++ 来写一个 Hello world
的程序扒 😄
#include<iostream>
int main() {
std::cout << "Hello world!" << endl;
return 0;
}
看不懂?没关系,我们一步一步看。
首先是最醒目的 #include<iostraem
。#
代表宏定义,这是要告诉编译器,这一行要优先编译。include
代表要让 c++ 提前加载尖括号里面的 iostream
文件。
如果你需要自己定义一个自己的头文件,请把它写成这样:
#include "head.h"
"
代表 c++ 编译器应该首先在当前目录查找,找不到才去标头路径查找,而尖括号内的头文件无法实现此功能。
然后是一行 int main()
。这里的 int
是一种数据类型,是 integer
的缩写,他代表这个函数的返回值是整形。 main
就是主要的的意思,这里是程序运行的入口。
括号里面一般是没有参数的,不过你可以写成 int main(void)
或 int main(int argc,char *argv[](**argv))
, 原因请问度娘。
然后是一对花括号,程序员一般把它写在一行的末尾,最好空一行;但是如果你是要学信奥的话一般把它写在第二行的最左边。括号里面的就是程序执行的内容啦!
std::cout
的意思是在命名空间 std
里面调用对象 cout
。命名空间是为了防止一些函数或对象重名才来使用的。当你要用到多次 std
的时候,你最好在前面加一句 using namespace std;
,这样后面所有有关 std
的东西就可以不写了!cout
是 c++ 的标准输出,需要用 <<
运算符进行输出,而引号里面的就是字符串。cout
会把字符串直接输出,显示在屏幕上。cout
还可以连续输出,用 <<
连接。endl
就是 cout
里面的换行,也可以用字符 '\n'
(字符是单引号)来代替,但 endl
是可以刷新缓冲区的,最好用 endl
。
下一行 return 0;
就是指这个函数是要返回零的,如果运行到这一行,不管后面还有什么代码,直接结束主函数。(main
函数不可以返回其他值哟~)
关于 c++ 写代码的好习惯
-
每一行都需要一个分号,除非这一行特别长需要换行;
-
变量最好要有实质性的内容,如果没有就用单个字符代替,最好不要起特别长的名字;
-
main
函数的末尾一定要return 0;
,虽然有些编译器会帮你补上,但有风险; -
cin
一定是>>
,cout
一定是<<
; -
最好代码中空点格,虽然看着很松散,但是调代码特别好调(亲身经历)。
输入、运算符
上面一段只是输出一些固定的东西,而生活是千变万化的,所以我们就需要输“入”。c++ 为我们提供的标准输入是 cin
,而 cin
的运算符是 >>
,与 cout
恰恰相反。(不要搞混了哦!)下面就是 a+b 问题的代码:
#include<iostream>
using namespace std;
int main(void)
{
int a, b;
cin >> a >> b;
cout << a + b << endl;
return 0;
}
现在这个程序与刚才的是不是不一样了呢?
首先我们先来看 int a, b;
这一行,这表示我们声明了两个整形变量 a
和 b
,逗号是用来分隔的。当然,你也可以写成这样:
int a;
int b;
接着我们要输入他们,也就是 cin >> a >> b;
这一行。
最后我们要输出它们的和,这里用 +
运算符:
cout << a + b << endl;
最后结束 main
函数:
return 0;
怎么样?是不是特别简单?
c++ 的运算符:
名称 | 符号 | 作用 |
---|---|---|
加法运算符 | + | 返回两个值的和 |
减法运算符 | - | 返回两个值的差 |
乘法运算符 | * | 返回两个数相乘的积 |
除法运算符 | / | 返回被除数除以除数的商 |
模运算符 | % | 返回余数 |
逻辑与 | && | 若两边都为真返回真,否则返回假 |
逻辑或 | \ | \ |
逻辑非 | ! | 真返回假,假返回真 |
赋值运算符 | = | 将右边的值赋给左边,并返回赋完的值 |
加运算符 | += | 将自己的值加上右边的值,并返回自己 |
减运算符 | -= | 将自己的值减去做右边的值,并返回自己 |
乘运算符 | *= | 将自己的值乘上右边的值,并返回自己 |
除运算符 | /= | 将自己的值除上右边的值,并返回自己 |
模等于运算符 | %= | 将自己的值模上右边的值,并返回自己 |
成员运算符 | . | 访问类中的成员 |
长度运算符 | sizeof() | 返回括号中的字节长度 |
位与运算符 | & | 返回二进制与的结果 |
位或运算符 | \ | |
位异或运算符 | ^ | 返回二进制异或的结果 |
取反运算符 | ~ | 返回二进制取反的结果 |
位复合运算符 | 在位运算符后面添上= | 将当前变量对右边的值做相应的位运算并返回 |
下标运算符 | [ ] | 返回数组中的某一位 |
三目运算符? | ? | 类似于 if |
地址运算符 | &(只有一个参数) | 返回当前变量的内存地址 |
自增后缀运算符 | 变量++ | 先用这个变量,用完之后 +1 |
自减后缀运算符 | 变量– | 先用这个变量,用完之后 -1 |
自增前缀运算符 | ++变量 | 先把这个变量 +1,然后再用 |
自减前缀运算符 | –变量 | 先把这个变量 -1,然后再用 |
左移运算符 | << | 二进制位左移,低位用 0 补齐 |
右移运算符 | >> | 二进制位右移,低位忽略 |
怎么多?别着急,用的多自然就熟了。
C 语言输入输出(c++ 兼容)
在 c++ 之前的 C 语言,也是有输入输出的,但是由于 C 语言功能没有 c++ 丰富,所以输入输出自然也不一样。C 语言输入是这样的:
scanf("控制符",&变量一,...);
输出是这样的:
printf("控制符",变量一,...);
这是 C 语言的 a+b:
#include<stdio.h>
using namespace std;
int main(void) {
int a, b;
scanf("%d%d",&a,&b);
printf("%d",a+b);
return 0;
}
可以看出,输入输出发生了很大的变化。首先头文件变了,变成了 stdio.h
(在 c++ 里面也可以写成 cstdio
)我们慢慢来看:
scanf
函数里面的控制字符串里面的每一个 %...
都是对应着后面的参数的,%d
就是代表下一个输入的值我们要把它们转成整数。后面的参数每一个一定都要加上 &
,因为他要改变实际的值是需要地址的,而 C 语言没有引用变量(后面会讲)。
printf
的格式与 scanf
类似,都是一样的,不过变量不用加 &
。
scanf
的输入控制符不只有 %d
,还有这些:
符号 | 作用(也可做输出用) |
---|---|
%d | 读入整数 |
%ld | 读入 long 整数 |
%lld | 读入 long long 整数 |
%ulld | 读入 unsigned long long 整数 |
%c | 读入一个字符 |
%s | 读入一个字符串(char* ) |
%i | 读入 i i i 后面的数的字节的整数 |
%x | 读入一个十六进制整数 |
在 % 后面的整数 | 向右对齐 |
在 % 后面的负整数 | 向左对齐 |
直接写符号 | 表示读入对应的符号 |
%f | 读入单精度小数 |
%lf | 读入双精度小数 |
在保留小数方面,C 语言输入明显由于 c++:
#include<stdio.h>
using namespace std;
double a;
int main(void)
{
scanf("%lf",&a);
printf("它保留的三位小数是:%.3lf",a);
return 0;
}
3.1415926535
它保留三位小数是:3.141
在保留小数、向右对齐方面,一般用 scanf
和 printf
,其他用 cin
和 cout
。
PS:
在输入多的时候,cin
由于不仅需要检测参数的类型,还要与 scanf
绑定,因此特别慢。这时候你就需要用 scanf
了。但是也有两行可以优化 cin
的代码:
std::ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
不懂请自行问度娘。
数据类型
c++ 也提供了丰富的数据类型,下面是基础数据类型:
关键字 | 名称 |
---|---|
int | 整形 |
char | 单字符型 |
bool | 布尔型 |
float | 单精度小数 |
double | 双精度小数 |
下面是类型修饰符以及可以修饰的数据类型:
修饰符 | 可修饰类型 | 作用 |
---|---|---|
long | int , long int , double | 让数的精度变高 |
signed | int , long int , long long int , double | 声明这个类型既可以存正数也可以存负数 |
unsigned | int , long int , long long , double | 声明这个类型只能存正数 |
下面是其他头文件中拓展的数据类型:
头文件 | 类型名 | 作用 |
---|---|---|
cstring 或 string.h | string | 存储字符串 |
请百度 | 请百度 | 请百度 |
现在我们试验一下:
#include<iostream>
using namespace std;
int a;
char ch;
bool f;
float b;
double c;
int main(void)
{
cin >> a >> ch >> f >> b >> c;
cout << "整数是" << a << "字符是" << ch << "bool型是" << f << "单精度浮点型是" << b << "双精度浮点型是" << c << endl;
return 0;
}
分支结构
if
:如果括号里的条件满足,那么执行里面的内容。
if...else
:如果 if
条件满足执行里面的内容,否则执行 else
里面的内容。
if...else if...else
:如果 if
条件满足执行里面的内容,否则如果 else if
里面的条件满足,执行 else if
里面的内容, 否则执行 else
里面的内容。
注意:
if
可以没有 else if
和 else
,else
和 else if
也可以没有其中的一个,但是 else if
和 else
必须有 if
!
循环结构
while(表达式)
:如果表达式满足,那么执行内容,一直重复。
do...while(表达式)
:首先执行一次内容,然后判断表达式有没有满足,如果满足重复步骤。
for(表达式一;表达式二;表达式三)
:这是最复杂的循环语句,画个图啊:
--->如果不满足--->跳出循环
|
执行表达式一--->如果条件满足--->执行循环
^ |
|____________________|
大概就是这样的,比较适合从 1 − n 1-n 1−n 逐步递增的场景,需要多多理解。
数组
我们之前都是用单个单个的变量来输入的。那如果现在要你输入
n
n
n 个数呢?我们就 for(int i=1;i<=n;++i)
,但如果你输完之后要在输出呢?如果
n
≤
10000
n\le 10000
n≤10000 呢?难道你还需要定义
10000
10000
10000 个变量吗?
这是我们就需要运用到“数组”了。数组的定义方式是这样的:
数据类型 数组名[数组需要的元素大小]
在用的时候,你只需用下标运算符 []
就可以访问数组了。注意:
-
数组下标是从零开始的,不过你也可以从一开始赋值;
-
数组下标没有负数;
-
下标可以使任何变量,甚至是别的数组的其中一个元素;
-
数组下标只能是
int
类型。
试验一下:
#include<iostream>
using namespace std;
int main(void)
{
int n;
cin >> n;
int a[n];
for(int i=0;i<n;++i)
{
cin >> a[i];
}
for(int i=n-1;i>=0;--i)
{
cout << a[i] << " ";
}
return 0;
}
3
1 2 3
3 2 1
PS:
在 c++ 中,数组大小可以是一个变量,但是只能是一维的,而且不太稳定,本渴鹅亲测过。最好开一个固定的数组来用,虽然内存多了一些,但是稳定。
数组也是可以赋初始值的,如果你回赋初始值的话,[]
内的大小是可以不用写的。比如:
int a[] = {1,2,3,4,5};
多维数组
在 c++ 中数组也是可以多维的。比如这样:
#include<iostream>
using namespace std;
int a[1005][1005];
int main(void)
{
int n, m;
cin >> n >> m;
for(int i=1;i<=n;++i)
{
for(int j=1;j<=m;++j)
{
cin >> a[i][j];
}
}
for(int i=n;i>=1;--i)
{
for(int j=m;j>=1;--j)
{
cout << a[i][j] << " ";
}
cout << endl;
}
return 0;
}
2 3
1 2 3
2 3 4
4 3 2
3 2 1
函数
在 c++ 中,除了主函数和系统函数,你还可以定义自己的函数,我们可以把函数看做一个对变量做操作的方法。函数定义格式是这样:
返回值 函数名(参数类型 参数名,...)
{
函数体;
}
比如,我们可以定义一个返回 a+b 的值的函数:
int plus(int a,int b)
{
return a+b;
}
然后在主函数内调用它:
int main(void)
{
int a, b;
cin >> a >> b;
cout << plus(a,b) << endl;
return 0;
}
这里就相当于我们把实际参数 a
和 b
的值赋给了函数里面的形式参数 a
和 b
,然后以形式参数 a
和 b
的和返回了过来, 然后输出。
如果你的函数只是输出一些内容,没有返回值的话请把它的返回值写成 void
类型。
递归函数
先看 n n n 的阶乘的代码:
#include<iostraem>
using namespace std;
int work(int a)
{
if(a<=1)return 1;
return a*work(a-1);
}
int main(void)
{
int n;
cin >> n;
cout << work(n) << endl;
return 0;
}
看不懂?没关系,我们慢慢的把实际的算式捋出来(假设 n n n 是 4 4 4 ):
1.首先,我们往函数里面放了一个值 4 4 4 ,函数返回了 a × w o r k ( a − 1 ) a\times work(a-1) a×work(a−1) 表达式现在是 4 4 4 ;
2.然后,work 函数又返回了 a × w o r k ( a − 1 ) a\times work(a-1) a×work(a−1) ,与前面的数相乘,就是 4 × 3 4\times 3 4×3 ;
3.接着,里面的 work 函数又返回了 a × w o r k ( a − 1 ) a\times work(a-1) a×work(a−1) ,与前面的数相乘, 就是 4 × 3 × 2 4\times 3\times 2 4×3×2
3.最后,最里面的 a a a 已经是 1 1 1 了,work 函数返回 1 1 1 ,整个调用函数产生的式子就是 4 × 3 × 2 × 1 4\times 3\times 2\times 1 4×3×2×1 ,正好是 n ! n! n! 。
这就是一个最简单的递归调用过程,需要多练才能熟哟~
指针
- Update: 进行了一次大升级,内容更多了。
指针,是C语言中的一个重要概念及其特点,也是掌握C语言比较困难的部分。指针也就是内存地址,指针变量是用来存放内存地址的变量,在同一CPU构架下,不同类型的指针变量所占用的存储单元长度是相同的,而存放数据的变量因数据的类型不同,所占用的存储空间长度也不同。有了指针以后,不仅可以对数据本身,也可以对存储数据的变量地址进行操作。
指针描述了数据在内存中的位置,标示了一个占据存储空间的实体,在这一段空间起始位置的相对距离值。在 C/C++语言中,指针一般被认为是指针变量,指针变量的内容存储的是其指向的对象的首地址,指向的对象可以是变量(指针变量也是变量),数组,函数等占据存储空间的实体。 ——百度百科
光说说不懂,还是实践一下吧。👩💼
在介绍指针之前,我们需要了解一个特殊的运算符 &
。注意这里不能与逻辑与 &&
混淆,&
只有一个!!!之前也讲了,&
也表示位或运算,但那个东西太复杂了,后面再讲。&
一般会放在一个对象、变量的前面(注意只有一个,而且是变量在后,不是符号在后)。它的名字叫做 取地址符。🙉
如果你使用 cout
,那么取出来的地址就会被识别成十六进制输出;如果你使用 printf
,那么你是用的 %d
只会输出整数。想要输出十六进制,需使用 %x
。现在看示范程序:👀
#include <iostream>
using namespace std;
int a = 1145141;
int main() {
cout << &a << '\n';
return 0;
}
(可能)0x403010
接下来就是关键性的问题了,为什么是可能呢?这就要说说最底层的知识了。
计算机的内存是不断波动的,因此当你在不同情况下,程序申请的内存都有可能不相等。因此无法确定一个准确的值。
现在,有了这些前缀芝士🧀,指针的学习就可以开始了!
指针的定义🖕
指针的定义与变量差不多,指向什么类型的东西就用什么类型名定义。定义规则为:
(指针指向的类型) *(指针名);
就好比定义了:
int a;
那么指向 a a a 的指针就该定义成
int *p;
指针里面装的是地址,因此我们可以定义指针 p p p 指向 a a a :
int a;
int *p = &a;
但是——————*
运算符只对一个指针变量有效。因此这样定义:
int* a, b, c;
其实只有
a
a
a 是指针变量,其他的都只是普普通通的整数变量而已。所以,建议大家尽量把 *
写到靠近变量的那边,而非类型那边。
int *a, b, c; // 更好的方式是:int b, c, *a;
而 *p
就是通过指针
p
p
p 来访问
p
p
p 指向的值。这个值是可以更改的,也可以输出。
现在,使用指针来控制变量的值吧!
int a;
int *p = &a;
*p = 1145141;
cout << a << ' ' << *p << '\n';
1145141 1145141
我们可以发现,a
和 *p
输出出来其实是同一个数,而第三行我们对
p
p
p 指向的值,也就是存储
a
a
a 的值改为了
114514
114514
114514 ,因此原本没有值的变量
a
a
a 被赋予了值。现在还有一段代码:
int *n = new int;
这段代码又是做什么的呢?这里运用了内存申请符 new
来申请了一个 int
大小的空间,并把这个地址赋予了
n
n
n 。注意:不要这样做:
int n = new int; // 这里的n是变量,定义时就已经有了空间,这样子会引发编译错误。
现在想必大家已经知道指针的只因本用法了吧?后面我会继续更新更高难度的指针操作,只因请期待!
类与对象
在 c++ 中,有很多基础数据类型。但我们能不能在这些基础数据的基础上,定义出我们自己的“类型”呢?当然可以,包括 cstring
里的 string
,iostream
里面的 istream 和 ostream,都是“类”。现在,我们来定义一个学生的类,类中有设置属性、获取属性的一些方法:
#include<iostream>
#include<cstring>
using namespace std;
class student
{
private:
int score, id;
string name;
public:
int newStudent(string name, int score, int id)
{
this->score = score;
this->id = id;
this->name = name;
}
string getName(void)
{
return this->name;
}
int getScore(void)
{
return this->score;
}
int getId(void)
{
return this->id;
}
}haokee("haokee", 100, 7), bluemoon("bluemoon", 0, 1);
int main(void)
{
cout << haokee.getName() << ":score:" << haokee.getScore() << "id:" << haokee.getId() << endl;
cout << bluemoon.getName() << ":score:" << bluemoon.getScore() << "id:" << bluemoon.getId() << endl;
return 0;
}
可以看到,类里面分了两大块:private
和 public
。其实应该是三大块:
private
:只有在类里面才可以访问的成员。
protected
:只有在类里面或子类才可以访问。
public
:在类内、类外或子类都可以访问。
这里我们写了一些成员函数用来返回当前类中的一些信息。this
指针指向的就是当前对象,而类的末尾我们定义了 haokee
和 bluemoon
两个对象, 并给了他们初始值。然后我们通过调用成员函数的方式输出了他们的信息。
在调用对象中的成员的时候,我们需要使用 .
运算符,这样程序才能知道是调用哪个对象中的那个成员。
->
运算符:他是通过指针来访问类中的元素,等同于:(*this).name
。
重载运算符
在类里面,我们不仅可以定义成员函数,还可以重载运算符。当符号两边的参数符合重载的参数时,就会自动调用重载的内容。
- 注意:如果在类里面重载,那么是不写第一个参数的,第一个参数就是
this
指针。
我们可以在刚才那个程序的 main
的前面写:
ostream &operator<<(ostream &out, const student &stu)
{
cout << stu.getName() << ":score:" << stu.getScore() << "id:" << stu.getId() << endl;
return in;
}
这里我们重载了左参数为 ostream
、右参数为 student
的 <<
,每当被调用的时候,他就会执行里面的内容,返回 out
对象是因为这样就可以连续输出了。所以输出就可以直接写为:
cout << haokee << bluemoon;
继承
在 c++ 里面,可以定义类的“儿子”。比如我们定义一个 haokee
类:
class haokee {
public:
void out(void) {
cout << "我是haokee" << endl;
}
};
然后我们在定义一个 haokee
类的派生类 son
:
class son : public haokee {
}S;
由于 S
是 son
类,是 haokee
类的“儿子”,所以 S
可以调用原来基类的函数:
S.out();
这时,protected
就有用了。你可以在派生类内访问一些成员,但是在类外无法访问,达到“保护”的效果。
class haokee {
protected:
int x = 1;
public:
void out(void) {
cout << "我是haokee" << endl;
}
};
class son : public /*标记*/ haokee {
public:
void out2(void) {
this->out(); //访问原类的公开成员
cout << x; //访问原类的保护成员
}
}S;
S.out2();
注意到继承时的注释标记没有?这里的 public
表示的是 ”继承类型“。一般都写 public
,但是也可以写其他的:
继承类型 | 符号 | 规则 |
---|---|---|
公有继承 | public | 除了私有成员,都可以访问 |
私有继承 | private | 基类的公有成员和保护成员都变成私有成员 |
保护继承 | protected | 基类的公有成员的保护成员都是派生类的保护成员 |
在调用基类的函数之上,我们也可以对基类的函数进行重写:
class haokee {
public:
void print(){cout << "我是好渴鹅";}
};
class bluemoon : public haokee {
public:
void print(){cout << "我不是好渴鹅,我是蓝月";}
}bl;
int main() {
bl.print(); //我不是好渴鹅,我是蓝月
return 0;
}
多继承
在 c++ 中,一个派生类也可以继承多个基类,中间用 ,
分隔。
class 派生类 : 继承类型 , 继承类型 基类, 继承类型 基类, ……
virtual
虚函数
class F {
public:
F(){}
void print(){cout << "父类方法";}
virtual ~F(){}
};
class S : public F {
public:
S(){}
void print(){cout << "派生类方法"} //重写了父类的方法
};
int main() {
F *f = new S();
f->print();
return 0;
}
可以看到,这里我们的指针类型与实际指向的地址的类型是不一样的。这是输出:
基类方法
但是我们明明实际指向的是派生类的指针,c++ 把他误以为是基类给输出了。现在我们把基类的函数改成这样:
virtual void print()
输出就会变成:
派生类方法
很显然,添加 virtual
可以区分基类和派生类的同名方法。
注意:
-
只用在基类加上
virtual
关键字,派生类会自动加上; -
析构函数必须也改成虚函数,不然会有对象释放错误的问题;
-
重写是对其他类的函数“改造”,而重载是在同一类同函数的不同参数列表的多个函数。
算法
照着附录(在后面)说的方法,打开洛谷(没登录的要先登录),点击 题库
页面,就可以看到一大堆题了。
我们点进第二题: A+B Problem \texttt{A+B Problem} A+B Problem ,就可以看到题面了。
A+B Problem
题目背景
强烈推荐新用户必读帖。
不熟悉算法竞赛的选手请看这里:
算法竞赛中要求的输出格式中,不能有多余的内容,这也包括了“请输入整数
a
\bm a
a 和
b
\bm b
b” 这一类的提示用户输入信息的内容。若包含了这些内容,将会被认为是 Wrong Answer
,即洛谷上的 WA
。在对比代码输出和标准输出时,系统将忽略每一行结尾的空格,以及最后一行之后多余的换行符。
若因此类问题出现本机(看起来)AC
,提交 WA
的现象,请勿认为是洛谷评测机出了问题,而是你的代码中可能存在多余的输出信息。用户可以参考在题目末尾提供的代码。
另外请善用应用中的在线 IDE 功能,以避免不同平台的评测中所产生的一些问题。
还有一点很重要的是,请不要在对应的题目讨论区中发布自己的题解,请发布到题解区域中,否则将处以删除或禁言的处罚。若发现无法提交题解则表明本题题解数量过多,仍不应发布讨论。
题目描述
输入两个整数 a , b a, b a,b,输出它们的和( ∣ a ∣ , ∣ b ∣ ≤ 10 9 |a|,|b| \le {10}^9 ∣a∣,∣b∣≤109)。
注意
- Pascal 使用
integer
会爆掉哦! - 有负数哦!
- C/C++ 的 main 函数必须是
int
类型,而且 C 最后要return 0
。这不仅对洛谷其他题目有效,而且也是 NOIP/CSP/NOI 比赛的要求!
好吧,同志们,我们就从这一题开始,向着大牛的路进发。
任何一个伟大的思想,都有一个微不足道的开始。
输入格式
两个以空格分开的整数。
输出格式
一个整数。
样例 #1
样例输入 #1
20 30
样例输出 #1
50
提示
本题各种语言的程序范例:
C
#include <stdio.h>
int main()
{
int a,b;
scanf("%d%d",&a,&b);
printf("%d\n", a+b);
return 0;
}
C++
#include <iostream>
#include <cstdio>
using namespace std;
int main()
{
int a,b;
cin >> a >> b;
cout << a+b << endl;
return 0;
}
Pascal
var a, b: longint;
begin
readln(a,b);
writeln(a+b);
end.
Python2
s = raw_input().split()
print int(s[0]) + int(s[1])
Python3
s = input().split()
print(int(s[0]) + int(s[1]))
Java
import java.io.*;
import java.util.*;
public class Main {
public static void main(String args[]) throws Exception {
Scanner cin=new Scanner(System.in);
int a = cin.nextInt(), b = cin.nextInt();
System.out.println(a+b);
}
}
JavaScript (Node.js)
const fs = require('fs')
const data = fs.readFileSync('/dev/stdin')
const result = data.toString('ascii').trim().split(' ').map(x => parseInt(x)).reduce((a, b) => a + b, 0)
console.log(result)
process.exit() // 请注意必须在出口点处加入此行
Ruby
a, b = gets.split.map(&:to_i)
print a+b
PHP
<?php
$input = trim(file_get_contents("php://stdin"));
list($a, $b) = explode(' ', $input);
echo $a + $b;
Rust
use std::io;
fn main(){
let mut input=String::new();
io::stdin().read_line(&mut input).unwrap();
let mut s=input.trim().split(' ');
let a:i32=s.next().unwrap()
.parse().unwrap();
let b:i32=s.next().unwrap()
.parse().unwrap();
println!("{}",a+b);
}
Go
package main
import "fmt"
func main() {
var a, b int
fmt.Scanf("%d%d", &a, &b)
fmt.Println(a+b)
}
C# Mono
using System;
public class APlusB{
private static void Main(){
string[] input = Console.ReadLine().Split(' ');
Console.WriteLine(int.Parse(input[0]) + int.Parse(input[1]));
}
}
Visual Basic Mono
Imports System
Module APlusB
Sub Main()
Dim ins As String() = Console.ReadLine().Split(New Char(){" "c})
Console.WriteLine(Int(ins(0))+Int(ins(1)))
End Sub
End Module
Kotlin
fun main(args: Array<String>) {
val (a, b) = readLine()!!.split(' ').map(String::toInt)
println(a + b)
}
Haskell
main = do
[a, b] <- (map read . words) `fmap` getLine
print (a+b)
Scala
object Main extends App {
println(scala.io.StdIn.readLine().split(" ").map(_.toInt).sum)
}
Perl
my $in = <STDIN>;
chomp $in;
$in = [split /[\s,]+/, $in];
my $c = $in->[0] + $in->[1];
print "$c\n";
很明显,实际就是输出 a + b a+b a+b ,根本就不用看程序范例,用我们之前学过的知识就可以轻松写出这样的代码:
#include <iostream>
using namespace std;
int a, b;
int main(int argc, char *argv[]) {
cin >> a >> b;
cout << a + b << '\n';
return 0;
}
然后点击 提交评测
,如果你的代码没有错误,应该会出现类似的界面:
然后点击右上角的头像(因人而异),点击 练习
就可以看到你做了哪些题目。
接着,我们写第二道题—— 超级玛丽游戏
吧!
题目很简单,就是让我们直接输出这个——
********
************
####....#.
#..###.....##....
###.......###### ### ###
........... #...# #...#
##*####### #.#.# #.#.#
####*******###### #.#.# #.#.#
...#***.****.*###.... #...# #...#
....**********##..... ### ###
....**** *****....
#### ####
###### ######
##############################################################
#...#......#.##...#......#.##...#......#.##------------------#
###########################################------------------#
#..#....#....##..#....#....##..#....#....#####################
########################################## #----------#
#.....#......##.....#......##.....#......# #----------#
########################################## #----------#
#.#..#....#..##.#..#....#..##.#..#....#..# #----------#
########################################## ############
大家伙。
但是如果我们使用 cout
一行一行地打,是要打很久的。
cout << " ********" << endl;
cout << " ************" << endl;
cout << " ####....#." << endl;
... ...
不过,c++ 会将相邻的两个字符串合并成一个,所以我们可以这样写:
printf(" ********\n"" ************\n"... ...)
然后我们再简单地换一下行,就可以变成这样子:
#include<stdio.h>
int main(int argc, char *argv[]) {
printf(
" ********\n"
" ************\n"
" ####....#.\n"
" #..###.....##....\n"
" ###.......###### ### ###\n"
" ........... #...# #...#\n"
" ##*####### #.#.# #.#.#\n"
" ####*******###### #.#.# #.#.#\n"
" ...#***.****.*###.... #...# #...#\n"
" ....**********##..... ### ###\n"
" ....**** *****....\n"
" #### ####\n"
" ###### ######\n"
"##############################################################\n"
"#...#......#.##...#......#.##...#......#.##------------------#\n"
"###########################################------------------#\n"
"#..#....#....##..#....#....##..#....#....#####################\n"
"########################################## #----------#\n"
"#.....#......##.....#......##.....#......# #----------#\n"
"########################################## #----------#\n"
"#.#..#....#..##.#..#....#..##.#..#....#..# #----------#\n"
"########################################## ############\n"
);
return 0;
}
但是这时还是不够简便,怎么办呢?
C++11 raw string literal! (破音
开头是 R"(
,结尾 )"
,中间的字符可以跨行,而且里面没有转义字符。不过中间不能穿插 )"
。怎么办呢?用全角符号!
可以这样写:
R"--(Embedded )" in string)--"
这样就很简单了:
#include<iostream>
using namespace std;
int main(int argc, char *argv[])
{
cout<<R"( ********
************
####....#.
#..###.....##....
###.......###### ### ###
........... #...# #...#
##*####### #.#.# #.#.#
####*******###### #.#.# #.#.#
...#***.****.*###.... #...# #...#
....**********##..... ### ###
....**** *****....
#### ####
###### ######
##############################################################
#...#......#.##...#......#.##...#......#.##------------------#
###########################################------------------#
#..#....#....##..#....#....##..#....#....#####################
########################################## #----------#
#.....#......##.....#......##.....#......# #----------#
########################################## #----------#
#.#..#....#..##.#..#....#..##.#..#....#..# #----------#
########################################## ############ )";
return 0;
}
输出第二个整数 - 洛谷
这一题很简单,按照之前我们学过的方法,进行操作。
C o d e \mathcal{Code} Code :
#include <iostream>
using namespace std;
int a, b, c;
int main() {
cin >> a >> b >> c; // 读入三个整数
cout << b << '\n'; // 输出第二个
return 0;
}
时间复杂度 O ( 1 ) \mathcal{O(1)} O(1) 。(这是一个时间复杂度, O \mathcal{O} O 里面表示的就是程序在最坏情况下需要运行的基本次数)
时间复杂度就是用来方便开发者估算出程序的运行时间。
我们该如何估计程序运行时间呢,我们通常会估计算法的操作单元数量,来代表程序消耗的时间, 这里我们默认 C P U \mathsf{CPU} CPU 的每个单元运行消耗的时间都是相同的。
假设算法的问题规模为 n n n ,那么操作单元数量便用函数 f ( n ) f(n) f(n) 来表示。
随着数据规模n的增大,算法执行时间的增长率和 f ( n ) f(n) f(n) 的增长率相同,这称作为算法的渐近时间复杂度,简称时间复杂度,记为 O ( f ( n ) ) O(f(n)) O(f(n)) 。
例如以下程序
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= n; ++j) {
cout << "1";
}
}
我们就说这个程序的时间复杂度是 O ( n 2 ) \mathcal{O(n^2)} O(n2) 。
计算 2 的幂 - 洛谷
题目大意
给定 n n n ,求出 2 n 2^n 2n 。
思路
暴力
2
n
2^n
2n 其实就是
n
n
n 个
2
2
2 相乘,因此我们可以这样写出一个 for
循环。
由于 2 0 = 0 2^0=0 20=0 ,因此需要特判:
int solve(int n) {
int ans;
if (n == 1) {
return 1;
}
for (int i = 2; i <= n; ++i) {
ans *= 2;
}
return ans;
}
事实上,我们可以使用位运算中的左移运算符( <<
)。它的操作是把一个数的二进制位全部往左移动
n
n
n 位,低位用
0
0
0 补齐。由于是二进制,因此使用 m << n
等效于
m
×
2
m
m\times 2^m
m×2m 。关于位运算更多的知识,请联系前面的基础知识。
int n;
cout << (1 << n); //由于cout也使用<<运算符,因此需要打上括号
Q
\mathcal{Q}
Q :int
不是有范围的吗?为什么不会爆?
A
\mathcal{A}
A :因为
n
≤
30
n\leq 30
n≤30 ,而 int
最大能存
2
31
−
1
2^{31}-1
231−1 ,因此不会爆炸。💥
浮点数向零舍入 - 洛谷
题意
给出 n n n ,将 n n n 向 0 0 0 取整(整数向下舍入,负数向上舍入)。
思路
很简单,由于 c++ 的类型转换自带说明的方法,因此可以使用强制类型转换或者直接输入 int
。
double n;
int main() {
cin >> n;
cout << long long(n) << '\n';
return 0;
}
long long n;
int main() {
cin >> n;
cout << n << '\n';
return 0;
}
与圆相关的计算 - 洛谷
首先,我们需要知道这三个公式:
d = 2 r d=2r d=2r
C = π d C=\pi d C=πd
S = π r 2 S=\pi r^2 S=πr2
依次完成计算即可。
const long double pi = 3.141592653589793238462643383279502884197169399375105820974944592307816406286208998628034825342117067982148086513282306647093844609550582231725359408128481117450284102701938521105559644622948954930381964428810975665933446128475648233786783165271201909145648566923460348610454326648213393607260249141273724587006606315588174881520920962829254091715364367892590360011330530548820466521384146951941511609433057270365759591953092186117381932611793105118548074462379962749567351885752724891227938183011949129833673362440656643086021394946395224737190702179860943702770539217176293176752384674818467669405132000568127145263560827785771342757789609173637178721468440901224953430146549585371050792279689258923542019956112129021960864034418159813629774771309960518707211349999998372978049951059731732816096318595024459455346908302642522308253344685035261931188171010003137838752886587533208381420617177669147303598253490428755468731159562863882353787593751957781857780532171226806613001927876611195909216420198938095257201065485863278865936153381827968230301952lf ;
long double r;
int main() {
cin >> r;
printf("%.4lf %.4lf %.4lf", r * 2, pi * r * 2, pi * r * r)
return 0;
}
在 OJ 中的基本事项我们已经学会了,那么就要开始疯狂刷题了。
这里比较推荐[题单广场](题单列表 - 洛谷),里面有很多的题可以刷。现在,我们讲讲顺序结构里面的几道题吧!
【深基2.例5】苹果采购
题目描述
现在需要采购一些苹果,每名同学都可以分到固定数量的苹果,并且已经知道了同学的数量,请问需要采购多少个苹果?
输入格式
输入两个不超过 1 0 9 10^9 109 正整数,分别表示每人分到的数量和同学的人数。
输出格式
一个整数,表示答案。保证输入和答案都在 int 范围内的非负整数。
样例 #1
样例输入 #1
5 3
样例输出 #1
15
十分简单,根据小学二年级所学的芝士,我们需要采购的苹果数量就等于 同学的数量
×
\times
× 每人分到的数量
。可以写出如下程序
#include <iostream>
using namespace std;
int a, b;
int main() {
cin >> a >> b;
cout << a * b << '\n';
return 0;
}
【深基2.例6】字母转换
题目描述
输入一个小写字母,输出其对应的大写字母。例如输入 q[回车] 时,会输出 Q。
输入格式
输出格式
样例 #1
样例输入 #1
q
样例输出 #1
Q
从题目意思来看,我们并没有处理字符串大小写的标准函数。但是我们需要知道 ASCII 码!
ASCII (American Standard Code for Information Interchange):美国信息交换标准代码是基于拉丁字母的一套电脑编码系统,主要用于显示现代英语和其他西欧语言。它是最通用的信息交换标准,并等同于国际标准 ISO/IEC 646。ASCII第一次以规范标准的类型发表是在1967年,最后一次更新则是在1986年,到目前为止共定义了128个字符。 ——百度百科
ASCII 码就是用整数来记录字符,因此不同字母的 ASCII 码其实是有规律的。ASCII 码有
128
128
128 个字符,但我们没必要全部记住。我们只需要知道大写转小写是
+
32
+32
+32 (这里的
+
+
+ 是加的 ASCII 值,会因为类型转换而变为字符,即
(空格)),小写转大写是
−
32
-32
−32 。
代码就不用说了。
#include <iostream>
using namespace std;
char a, b;
int main() {
cin >> a;
b = a - 32;
cout << b << '\n';
return 0;
}
[NOIP2017 普及组] 成绩
题目背景
NOIP2017 普及组 T1
题目描述
牛牛最近学习了 C++ 入门课程,这门课程的总成绩计算方法是:
总成绩=作业成绩 × 20 % + \times 20\%+ ×20%+小测成绩 × 30 % + ×30\%+ ×30%+期末考试成绩 × 50 % \times 50\% ×50%
牛牛想知道,这门课程自己最终能得到多少分。
输入格式
三个非负整数 A , B , C A,B,C A,B,C,分别表示牛牛的作业成绩、小测成绩和期末考试成绩。相邻两个数之间用一个空格隔开,三项成绩满分都是 100 100 100 分。
输出格式
一个整数,即牛牛这门课程的总成绩,满分也是 100 100 100 分。
样例 #1
样例输入 #1
100 100 80
样例输出 #1
90
样例 #2
样例输入 #2
60 90 80
样例输出 #2
79
提示
输入输出样例 1 说明
牛牛的作业成绩是 100 100 100 分,小测成绩是 100 100 100 分,期末考试成绩是 80 80 80 分,总成绩是 100 × 20 % + 100 × 30 % + 80 × 50 % = 20 + 30 + 40 = 90 100 \times 20\%+100 \times 30\%+80 \times 50\%=20+30+40=90 100×20%+100×30%+80×50%=20+30+40=90。
输入输出样例 2 说明
牛牛的作业成绩是 60 60 60 分,小测成绩是 90 90 90 分,期末考试成绩是 80 80 80 分,总成绩是 60 × 20 % + 90 × 30 % + 80 × 50 % = 12 + 27 + 40 = 79 60 \times 20\%+90 \times 30\%+80 \times 50\%=12+27+40=79 60×20%+90×30%+80×50%=12+27+40=79。
数据说明
对于 30 % 30\% 30% 的数据, A = B = 0 A=B=0 A=B=0。
对于另外 30 % 30\% 30% 的数据, A = B = 100 A=B=100 A=B=100。
对于 100 % 100\% 100% 的数据, 0 ≤ A , B , C ≤ 100 0≤A,B,C≤100 0≤A,B,C≤100 且 A , B , C A,B,C A,B,C 都是 10 10 10 的整数倍。
思路
对于一个百分数如 25 % 25\% 25% ,那可以化成 25 100 \dfrac{25}{100} 10025 ,也就是 0.25 0.25 0.25 。知道了这个规律,代码轻轻松松:
#include <cstdio>
using namespace std;
int a, b, c;
int s;
int main() {
scanf("%d%d%d", &a, &b, &c);
s = (int)(a * 0.2 + b * 0.3 + c * 0.5);
printf("%.lf", (double)s);
return 0;
}
附录
洛谷——步入 OI \text{OI} OI 大门
当你学完这些基础知识后,就可以去洛谷刷题了。首先,打开网站,是这个样子:
然后我们点一下右上角的 注册 按钮,就来到了用户注册界面。按照对应的填进去,你就注册成功了!
可以先试着摸索摸索功能。
然后点击“题库”,你就来到了题目的海洋。
随便点进一个题目进去,点击提交答案,就可以提交你的代码哦!😘
注意:洛谷的题目
99.99
%
99.99\%
99.99% 都是使用的标准输入输出(stdio
),而非文件输入输出。
配置 OI \text{OI} OI 刷题环境
最开始的时候,老师一般都会给我们使用 Dev-c++ ,但是 Dev-c++ 已经停更很多年了,调试功能也有一些缺陷。况且现在一般 c++ 标准都是 c++14 ,而 Dev-c++ 是 c++11。可能好用的原因就是它可以支持单文件编译运行吧。。。
目前主流的高颜值、高扩展性的 IDE 应该就是微软的。
微软的 IDE 主要是 Visual Studio \texttt{Visual Studio} Visual Studio 或者 Visual Studio Code \texttt{Visual Studio Code} Visual Studio Code 。前者更适合大型项目,而后者一般编译单文件。
Visual Studio \texttt{Visual Studio} Visual Studio
下载链接:官网
下载之后打开它,就会弹出安装界面。有各种各样的工作负载可以选,但 c++ 开发一般只用 使用c++的桌面开发
和 通用Windows平台开发
,其他的酌情选择。勾选上,再改一改安装路径,尽量别安装在 C 盘上,避免卡得要死。
安装包可能有点大,等一小会就好了。
装完之后点击启动,然后没有项目的话就点击 创建新项目
。
我们选择 控制台应用
。输完相关信息后就来到了编辑区域:
当然,这款软件支持各种拓展以及颜色配置,感兴趣的话可以去网上找一找。(我的 VS 是装了插件,所以可能颜色跟你们不一样)
使用事项(避雷)
-
千万不要直接在资源管理器里面新建文件,不然项目里面是看不到的。
-
如果想只生成一个源代码,那么右键其他的所有源文件,点
属性
,把从生成中排除
改为是。 -
一般情况下不要调试,按
以非调试模式运行
。调试器的内存占用实在是太大了!
这就是一个窗口的内存大小,大约 1.7 GB 1.7~\text{GB} 1.7 GB 。请保证你的电脑有 8 GB 8~\text{GB} 8 GB 的内存空间,不然用起来会有点卡。
Visual Studio Code \texttt{Visual Studio Code} Visual Studio Code
一样,还是去官网下载。
点击 我同意此协议
(应该没有人认真看吧?),然后下一步,这些选项酌情选择,不会太影响后续使用。最后安装!
安装好后打开它(你们应该是英文的):
我们先点开左边的“拓展按钮”,搜索 chinese
,点击 Install
,它会叫你 Restart
,按照它的操作走就行了。
但是 VS Code \texttt{VS Code} VS Code 没有安装编译器,所以你需要自己安装。
官网下载较慢,可以试试这个链接:MinGW-w64 - for 32 and 64 bit Windows - Browse Files at SourceForge.net
然后我们解压一下(记住解压的路径),就需要配置环境变量。
打开文件资源管理器,右键此电脑,点击属性,点击高级系统设置,双击 Path 系统变量,点击新建,值为 解压的路径\bin\
文件夹。然后一路点击
×
\times
× 或者 确定,一直点到没有窗口就行。
然后我们测试一下是否安装正确。按 Win + R
键打开运行,输入 cmd
回车,就来到了命令行。输入 gcc -v
或者 g++ -v
,如果没有报错则说明安装完毕 。
最后打开 VS Code ,安装 Code Runner
插件,然后写好代码并保存,点击右上角的运行按钮旁边的下切按钮,调成 code runner
之后再按运行按钮,就可以看到运行结果了!
做黑框框小游戏的函数:
使用头文件:
#include<windows.h>
#include<conio.h>
#include<ctime>
#include<cstdlib>
函数:
void cls(void) // 清屏
{
system("cls");
}
char get(void) // 返回按下的键
{
return getch();
}
int random(int minN,int maxN) // 在他俩中间随机选一个值
{
srand((unsigned)time(NULL));
return rand()%(maxN-minN)+minN;
}
void sleep(double s) // 原函数是毫秒,我这里为了方便给他改成秒
{
Sleep(s*1000);
}
void color(signed ForgC = 0, signed BackC = 15) //设定从设定开始的前景色和背景色
{
WORD wColor = ((BackC & 0x0F) << 4) + (ForgC & 0x0F);
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), wColor);
}
void pause(void)
{
system("pause");
}
写了半天,点个赞吧~😜