算法竞赛C++快速入门

2hard4me! / 2024-10-23 / 原文

算法竞赛C++快速入门(施工中)

仅针对xcpc的cpp教学!
c++是 c语言的一个超集,也就是说c++包含了c语言的所有特性,但是c++又有很多c语言没有的特性,比如面向对象编程。

看完这个再配合c++STL的学习,你就可以开始刷题了!

对于算法竞赛来说,我们主要使用c++的c语言部分和STL(标准模板库)部分,所以我们实际上学的是C+STL()

STL之后再开帖子详细介绍喵~

目录

  • 算法竞赛C++快速入门(施工中)
    • 基础语法
      • 前置知识
        • 注释
    • 计算机基础知识(也许你们需要)
    • 基本的常用数据类型
    • 变量作用域(feel it)
    • C++ 常量
    • c++ 运算符
    • c++条件语句
    • c++循环语句
    • c++函数
    • 数组和字符串
    • 引用和指针
    • 结构体和类
    • STL初步

基础语法

这个是一个简单的C++程序。如果你目前看不懂是什么意思,没关系,只要feel it

#include <iostream>//头文件,包含输入输出流
using namespace std;
//命名空间,使用std命名空间才能用cout和cin

int main(){//主函数,必须有
    //cout 包含在iostream中
    cout<<"Hello World!"<<endl;//输出流,输出Hello World!
    return 0;
}

一个典型的c++程序要包含头文件,有一个主函数

What is 头文件?

头文件是一种特殊的文件,它包含了一些函数的声明,变量的声明,宏定义等。头文件的文件名通常以.h结尾,头文件的内容通常包含在#ifndef、#define、#endif的条件编译指令中,以防止头文件被重复包含。头文件的作用是将程序中经常使用的函数的声明、变量的声明等放在一个文件中,以便在需要的时候包含到程序中,以简化程序的编写和维护。

What is 命名空间?

命名空间是C++中的一种重要的机制,它可以用来避免命名冲突。在C++中,每个命名空间都是一个作用域,命名空间中的名字只在该命名空间中有效。命名空间可以嵌套,一个命名空间可以包含另一个命名空间

刚刚的程序如果不使用using namespace std;命名空间,那么就要这样写

#include <iostream>

int main(){
    
    std::cout<<"Hello World!"<<std::endl;

    return 0;
}

我们打算法竞赛的时候,一般都会使用using namespace std;
但是显然,在工程项目里这不是一个好习惯!(\(×1\)

前置知识

注释

//这是单行注释

/*
    这是
    多行
    注释
*/

流是C++中的一个重要概念,它用来处理输入输出的。C++中有两种流:

  • 输入流cin
  • 输出流cout

其他的流还有cerr和clog,这个以后再说()

当然,你写算法的时候,也可以使用printf,scanf,但是cin,cout更加方便。
但是cin和cout慢,所以在一些时间复杂度比较极限的题中,可能会使用printf和scanf。

更快的还有手写的快读快写,这个以后再说()

下面是输入输出的例子,FEEL IT

#include <iostream>
#include <string>
using namespace std;
int main(){

    
//-----------------------
    int a,b,c,d;
//链式输入输出
    cin>>a>>b>>c>>d;//输入 10 100 1000 1
    /*等价于
    cin>>a;
    cin>>b;
    cin>>c;
    cin>>d;
    */
   //输入输出可以用空格,回车,tab等分隔符分隔,

    cout<<a+b+c+d<<endl;//输出 1111
    //endl是换行符,类似于\n
    //唯一的区别是endl会刷新缓冲区,而\n不会
//-----------------------
    string s;

    cin>>s;//输入 hello world
    cout<<s<<endl;//输出 hello
//-----------------------




    while(cin>>a){//会一直读入直到遇到0
        if(a==0) break;
        cout<<a<' '<<endl;
        //输入 1 2 3 4 5 0
        //输出 1 2 3 4 5
    }
    string tmp="MNS"
    cout<<"A"<<" "<<a<<" O.o "<<tmp<<endl;
    //-----------------------
    
    while(cin>>a){//会一直读入直到遇到EOF(文件结束符)
        cout<<a<<endl;
    }
    return 0;
}

计算机基础知识(也许你们需要)

基本的常用数据类型

不同于python的动态类型,C++是静态类型语言,变量在声明时必须指定类型。并且C++的数据类型是有范围的,所以在使用时要注意数据类型的选择。
实际上python的整数的底层原理是用数组存储每一位数字再模拟加减乘除,而C++的整数是直接存储的二进制数,所以C++的整数范围是有限的。

!!!不同平台的数据类型的范围可能有所不同,这里只是通常情况

  • int

    32位整型,范围是 \(-2^{31} 至 2^ {31} -1\)

    \(2^ {31} -1\) = 2147483647

    如果 此时+1 会变成-2147483648(溢出了)

  • long long

    64位整型,范围是 \(-2^{63} 至 2^ {63} -1\)

  • unsigned int

    32位无符号整型,与int不同的是二进制中最高位不是符号位,也用来表示值。
    范围是 \(0 至 2^ {32} -1\)

  • unsigned long long

  • float

  • double

  • char

  • string

  • bool

变量作用域(feel it)

你们应该已经在其它语言里学过了变量的作用域,这里再复习一下


#include <iostream> 
using namespace std;
int otto=114514;

void func(){
    int otto=666666;
    cout<<otto<<endl;
    cout<<::otto<<endl;
}

int main(){
    int otto=1919810;
    cout<<otto<<endl;// 优先使用局部变量,输出1919810
    cout<<::otto<<endl;// ::是作用域解析运算符,表示全局变量,输出114514

    func();
    return 0;
}

作用域是程序的一个区域,变量的作用域可以分为以下几种:

局部作用域:在函数内部声明的变量具有局部作用域,它们只能在函数内部访问。局部变量在函数每次被调用时被创建,在函数执行完后被销毁。

全局作用域:在所有函数和代码块之外声明的变量具有全局作用域,它们可以被程序中的任何函数访问。全局变量在程序开始时被创建,在程序结束时被销毁。

块作用域:在代码块内部声明的变量具有块作用域,它们只能在代码块内部访问。块作用域变量在代码块每次被执行时被创建,在代码块执行完后被销毁。

类作用域:在类内部声明的变量具有类作用域,它们可以被类的所有成员函数访问。类作用域变量的生命周期与类的生命周期相同。

!!ATTENTION!!:如果在内部作用域中声明的变量与外部作用域中的变量同名,则内部作用域中的变量将覆盖外部作用域中的变量。


正确地初始化变量是一个良好的编程习惯!,否则有时候程序可能会产生意想不到的结果。

C++ 常量

暂时不展开喵

c++ 运算符

算术运算符

+	把两个操作数相加	A + B 将得到 30  

-	从第一个操作数中减去第二个操作数	A - B 将得到 -10  

*	把两个操作数相乘	A * B 将得到 200  

/	分子除以分母	B / A 将得到 2  !!这个是整除,向下取整!! 

%	取模运算符,整除后的余数	B % A 将得到 0  

++	自增运算符,整数值增加 1	A++ 将得到 11  

--	自减运算符,整数值减少 1	A-- 将得到 9  



注意!C++中的自增自减运算符有前置和后置两种形式,区别在于前置形式是先运算再赋值,后置形式是先赋值再运算。
比如

int a=1;
int b=++a;//b=2,a=2 前置自增先 加   再 赋值
a=1;
int c=a++;//c=1,a=2 后置自增先 赋值 再 加

关系运算符,没什么好说的

==	检查两个操作数的值是否相等,如果相等则条件为真。	(A == B) 为假。

!=	检查两个操作数的值是否相等,如果值不相等则条件为真。	(A != B) 为真。

>	检查左操作数的值是否大于右操作数的值,如果是则条件为真。	(A > B) 为假。

<    检查左操作数的值是否小于右操作数的值,如果是则条件为真。	(A < B) 为真。

>=	检查左操作数的值是否大于或等于右操作数的值,如果是则条件为真。	(A >= B) 为假。

<=	检查左操作数的值是否小于或等于右操作数的值,如果是则条件为真。	(A <= B) 为真。

**
逻辑运算符**

&&	称为逻辑与运算符。如果两个操作数都非零,则条件为真。	(A && B) 为假。

||  称为逻辑或运算符。如果两个操作数中有任意一个非零,则条件为真。	(A || B) 为真。

!	称为逻辑非运算符。用来逆转操作数的逻辑状态。如果条件为真,则逻辑非运算符将使其为假。	!(A && B) 为真。

但是你用 and or not 也是可以的

致敬传奇奇技淫巧:位运算符

为了方便演示

设:
A=60=0011 1100
B=13=0000 1101

&	
按位与操作,按二进制位进行"与"运算。运算规则:

0&0=0;   
0&1=0;    
1&0=0;     
1&1=1;
(A & B) 将得到 12,即为 0000 1100

|	
按位或运算符,按二进制位进行"或"运算。运算规则:

0|0=0;   
0|1=1;   
1|0=1;    
1|1=1;
(A | B) 将得到 61,即为 0011 1101

^	
异或运算符,按二进制位进行"异或"运算。运算规则:

0^0=0;   
0^1=1;   
1^0=1;  
1^1=0;
(A ^ B) 将得到 49,即为 0011 0001
(你可以把"异或"理解成不进位加法)
~	
取反运算符,按二进制位进行"取反"运算。运算规则:

~1=-2;   
~0=-1;
( ~A ) 将得到 -61,即为 1100 0011,一个有符号二进制数的补码形式。

<<	二进制左移运算符。将一个运算对象的各二进制位全部左移若干位(左边的二进制位丢弃,右边补0)。	A << 2 将得到 240,即为 1111 0000

>>	二进制右移运算符。将一个数的各二进制位全部右移若干位,正数左补0,负数左补1,右边丢弃。

杂项运算符 (了解即可)

sizeof	

sizeof 运算符返回变量的大小。例如,sizeof(a) 将返回 4,其中 a 是int。

Condition ? X : Y	

条件运算符。如果 Condition 为 True ? 则值为 X : 否则值为 Y。

,
逗号运算符会顺序执行一系列运算。整个逗号表达式的值是以逗号分隔的列表中的最后一个表达式的值。
.(点)和 ->(箭头)	成员运算符用于引用类、结构和共用体的成员。

Cast	
强制转换运算符把一种数据类型转换为另一种数据类型。例如,int(2.2000) 将返回 2。

&	
指针运算符 & 返回变量的地址。例如 &a; 将给出变量的实际地址。

*	
指针运算符 * 指向一个变量。例如,*var; 将指向变量 var。

c++条件语句

cin>>a;
if(a==3){
   //do something
}
else if(){

}
else if(){

}
else{

}

还有一个三目运算符

int a=1,b=2;
int c=(a>b)?a:b;
//c=2
//等价于
if(a>b){
    c=a;
}
else{
    c=b;
}

还有一个switch case

int a=1;
switch(a){
    case 1:
        //do something
        break;
    case 2:
        //do something
        break;
    default:
        //do something
        break;
}

c++循环语句

经典for while do-while 三种循环语句

for(int i=0;i<10;i++){
    
}
//你也可以省略for的参数

for(;;){
    //无限循环

    break;//跳出当前层循环
    continue;//跳过本次循环
}


while(){
    //do something
}


do{
    //do something
}while();

c++函数

函数是一组一起执行一个任务的语句。每个 C++ 程序都至少有一个函数,即主函数 main(),所有简单的程序都可以定义其他额外的函数。
函数声明告诉编译器函数的名称返回类型参数
函数定义提供了函数的实现,即函数的实际主体。

#include <iostream>
using namespace std;

void func();//函数声明

//函数名前是返回类型,void表示没有返回值
//返回值也可以是int,double或者自定义类型

void func(){//函数定义
    cout<<"Hello World!"<<endl;
}

int main(){
    func();//函数调用
    return 0;
}

要注意的是,函数声明和函数定义可以分开,但是函数定义必须在函数调用之前。


//这是示例喵,把定义和声明分开是一个好习惯


void func(int a);//函数声明

int main(){
    func(1);
    return 0;
}

void func(int a){//函数定义
    cout<<a<<endl;
}

函数的参数可以是值传递,引用传递,指针传递。这个以后再说()

//函数的 参数的类型,数量 和 函数的名称 唯一确定了一个函数
//函数的重载是指函数名相同,但是参数不同的函数

void func(int a){
    cout<<a<<endl;
}
void func(double a){
    cout<<a<<endl;
}//这两个函数是不同的函数

int main(){
    //编译器会根据参数的类型自动选择调用哪个函数
    func(1);
    func(1.1);
    return 0;
}



函数可以没有参数
main函数也是一个函数,它的参数是命令行参数,但是我们一般不用(


void func(){
    cout<<"Hello World!"<<endl;
}

其实cpp的函数还有很多特性,比如函数指针,函数模板,lambda表达式,虚函数等等,这个以后再说()

数组和字符串

引用和指针

结构体和类

STL初步