一、为啥要转C++:之前只学过C语言,然后为了蓝桥杯苦哈哈敲了C好久,偶然听了别人的意见去看了一下C++后发现简直太香了——C++不像C要把底层逻辑自己敲出来,而是可以直接调用STL函数库(比如排序,C语言可能要敲个几十行代码C++直接用调用sort函数就可以了),而且C++完全兼容C语言,里面的语法全部可以用C替换。Now it‘s time to总结一下C++语法了。
二、#include<iostream>输入输出流,相当于C的#include<stdio.h>。
using namespace std;使用标准命名空间。引用这个头文件就不需要在代码每次用到标准库中的函数时都加上std::前缀,坏处是大量使用标准库时滥用该头文件可能会导致命名冲突和代码的不可读性(小小蓝桥杯不用考虑这个)。
一般C++的头文件都和C很像,就是要把C后面的.h换成C++前面的c。(如#include<string.h>——>#include<cstring>)
三、bool变量:true和false。适合标记只存在两种状态时用,如岛屿个数那道题需要标记陆地或岛屿是否访问过(是则标记为true,不是则为false),这样可以避免重复访问。
四、string类:
1、定义:string s=“hello”; string s2=“ world”;
2、拼接:string.s3=s+s2;(输出为hello world)是的直接加就行了,是不是很方便
3、输入/输出:
3.1cin>>s cout<<s
3.2 getline(cin,s):解决直接cin无法读取空格及之后内容的缺陷getline能完地获取一行的信息(以换行符结束)。
4、长度:可以直接使用s.length()获取s的长度而不需要知道s到底存有多少个字符或数(与C比较方便性立显),经常与for搭配使用(作为终止条件)
5、子串:s1=s.substr(n,m)在s串中从下标为n的地方开始往后取m位
s2=s.substr(n)在s串中从下标为n的地方开始后面内容全部取完
6、结构体:与C相比在main()中调用struct时可以省去‘struct’而直接用结构体名称来命名我们的变量。
7、引用:自定义函数去引用main()中定义好的值(加&)输出时会改变main()中的值,反之则不会改变main()中的值
函数中的a、b没引用(没加&):
引用(加&):
跟C语言中传值和传址的区别类似。
8、vector:容器,实现自动内存管理。
(vector<存放内容的类型>该容器的名称(分配n个空间,全部初始化为m))
8.1 vector<int>v(20,1):给v分配20个空间,每个空间初始化为2;
8.2 vector<int>v(20):给v分配20个空间,每个空间初始化为0;
8.3 vector<int>v:不手动分配空间,v根据存储内容自动分配空间。
迭代器:for(auto p=v.begin();p!=v.end();p++){
cout<<*p;}// 无需在意数组内容变动或存储空间变动,用迭代器实现直接遍历auto p中的p可以看成指针,所以输出时要进行取地址操作
9、set:是一个集合,无法为其分配空间,里面元素全部互异且会自动进行从
小到大的排序。排序的依据是每个元素的ASCII码值。希望不进行排序的话应
使用unorderd_set。
9.1创建:set<int>s;(不能分配空间)
9.2插入:s.insert(data);
9.3查找:s.find(data);
9.4删除:s.erase(data);从s中遍历寻找data,找到则删除
(如果编译的时候for输出的时候出现错误多半是c++不支持auto等函数可以
在编译选项-编译器-加入以下命令:-std=c++,然后应该就可以正常运行了)
10、map:键值对,会自动将所有的值按照键的ASCII码值从小到大排序。想保留原输入顺序的可以使用unorderd_map; Map<类型1(键),类型2(值)>函数名;
10.1:添加 m[“hello”]=2; m[“ world”]=3;
10.2:遍历(迭代器):for(auto p=m.begin();p!=m.end();p++)
10.3:获取长度:m.size();
11、stack(栈):特点是先进后出,只能对栈顶元素进行操作,所以无法使用迭代器。
11.1创建:stack<类型>名称 (stack<int>s);
11.2压栈:s.push(data);
cout<<s.top();//访问栈顶元素
11.3出栈:s.pop();//弹出栈顶元素
11.4获取长度:s.size();
12、queue(队列):特点是先进先出,可以对队首和队尾两个地方进行操作,可以使用迭代器。
12.1创建:queue<类型>名称 (queue<int>s)
12.2入队:s.push(data);
12.3出队:s.pop();
12.4访问:队首:s.front(); 队尾:s.back();
12.5获取长度:s.size();
13、bitset(位运算):方便处理二进制。将二进制从低位到高位以此存储,所以for循环输出的b[i]与直接输出b的结果相反。
13.1初始化:bitset<5>b(s.pos,n);在s串中从第pos为开始向后取n位
bitset<5>b;
bitset<5>b(19)
bitset<8>b(11)
不够放截断,放不满补0.
13.2用法 (以下的第几位均是从二进制的低位到高位数)
b.any():b是否有1;
b.none():b是否不存在1;
b.count():b中1的个数
b.size():b中元素个数
b.test(i):检查下标为i处的元素是否为1
b.set(i):将下标为i的元素置为1
b.reset():所有位归0;
b.reset(i):第i位归零
b.flip():所有位取反
b.flip(i):第i位取反
14、sort(排序):对数组(arr[])或容器(vector)按照规则排序。(若没有设定规则则默认从小到大排)
形式:sort(首元素,尾元素,排序规则)//规则函数只能为bool类型
vector用v.begin()表示首元素,v.end()表示尾元素;
int arr[]用arr表示首元素,arr+n表示尾元素
#include <iostream>
#include<bits/stdc++.h>
#include <bitset>
using namespace std;
bool cmp(int a, int b) {
// 如果 a 和 b 都是偶数或都是奇数,则比较它们的大小
if ((a % 2 == 0 && b % 2 == 0) || (a % 2 != 0 && b % 2 != 0)) {
return a > b; // 从大到小排列
}
// 如果一个是偶数一个是奇数,偶数在前,奇数在后
return a % 2 == 0;
}
int main() {
int arr[20];
int i=0;
for(int j=10;j>=0;j--){
arr[i]=j;
i++;
}
for(int i=0;i<=10;i++){
cout<<arr[i]<<" ";//原始数组
}
cout<<endl;
sort(arr,arr+10);
for(int i=0;i<=10;i++){
cout<<arr[i]<<" ";//没设置规则默认从小到大排序
}
cout<<endl;
sort(arr,arr+10,cmp);
for(int i=0;i<=10;i++){
cout<<arr[i]<<" ";//调用上面的cmp规则进行排序
}
}
15、cctype头文件常用函数
isalpha():是否是字母
islower():是否为小写
isupper():是否为大写
isalnum():是否是数字/字母(二者满足其一即为true)
isspace():是否是space/t/r/n
tolower():大写转小写
toupper():小写转大写
16、auto:auto 是一个关键字,用于声明变量时自动推导其类型。使用 auto可以使编程更加简洁,尤其是在涉及复杂类型或模板类型时。(注意:用auto的变量必须要初始化,不然人家怎么推类型)
如果你有一个复杂的类型声明,可以使用 auto 来自动推导其类型,而无需显式指定类型。
auto x = 10; // 推导 x 为 int 类型
auto y = 3.14; // 推导 y 为 double 类型
auto z = "Hello"; // 推导 z 为 const char* 类型
auto 还可以与迭代器一起使用,使代码更加简洁。例如:
17、基于范围的for循环
传值:for(int i:arr):这条语句是C++11引入的一种新的循环语法,称为范围(range-based)for循环,用于遍历容器或数组中的元素。具体来说,for(int i : arr)的意思是对数组 arr 中的每个元素,将其依次赋值给变量i,然后执行循环体中的代码块。在这里,int i 是循环中的迭代变量,用于存储数组 arr中的每个元素的值。arr则是需要遍历的数组。循环将依次遍历数组中的每个元素,并在每次迭代时将当前元素的值赋给变量i,然后执行循环体中的代码块。
传值操作不改变原数组的值:
传址:for(int &i:arr):这条语句也是C++11引入的范围(range-based)for循环的一种形式,与前面提到的 `for(int i : arr)` 稍有不同。在这里,`int &i` 表示 `i` 是一个整型的引用变量,而不是一个拷贝,这意味着在循环中对 `i` 的修改会影响到数组 `arr` 中对应位置的值。`for (int &i : arr)` 的意思是对数组 `arr` 中的每个元素,将其依次作为整型的引用赋值给变量 `i`,然后执行循环体中的代码块。这种引用形式的循环在需要修改数组元素的值时非常有用,因为它可以直接操作原始数组而无需拷贝数组元素的值。但需要注意的是,由于是引用形式,如果在循环中修改了 `i` 的值,将会直接修改数组 `arr` 中对应位置的值。
传址操作会改变原数组的值:
推广:for(auto i:v): 这条语句也是C++11引入的范围(range-based)for循环的一种形式,其中使用了关键字 `auto` 来自动推断迭代变量的类型。在这里,`auto i` 表示编译器会根据被遍历的容器 `v` 的类型来确定迭代变量 `i` 的类型,从而避免了手动指定类型的麻烦。`for(auto i : v)` 的意思是对容器 `v` 中的每个元素,将其依次赋值给自动推断出的变量 `i`,然后执行循环体中的代码块。这种自动推断类型的循环语法在处理不同类型的容器时非常方便,因为无需手动指定迭代变量的类型,编译器会根据容器的类型自动推断。
蓝桥杯必备的一些模板:
一上手就应该敲的:
#include<bits/stdc++.h>
#define int long long
#define deb(x)cout<<#x<<"="<<x<<'\n';
#define endl '\n'
using namespace std;
signed main(){
std::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
int t=1;
while(t--)
solve();
}
内容:
//判断n是否为素数:
bool is_prime(int n){
if(n<=1)return false;
for(int i=2;i<=sqrt(n);i++){
if(n%i==0)return false;
}
return true;
}
//求最大公约数:
int gcb(int a,int b){
return b==0?a:gcb(b,a%b);
}
//最长递增子序列:
for(j=1;j<len;j++){
for(i=0;i<j;i++){
if(a[j]>a[i]&&dp[j]<dp[i]+1)
dp[j]=dp[i]+1;
}
}
//X的二进制长度
int bitlength(int x){
int d=0;
while(x>0){
x>>=1;
d++
}
return d;
}
//质因数分解(举个例子,考虑数 12。它可以被分解为 2 × 2 × 3,
//其中的因数 2 和 3 都是质数,所以 2 和 3 就是 12 的质因数。
//一般来说,质因数分解是将一个数分解为一系列质数的乘积的过程。):
int reduce(int prime[],int pn,int n,int rest[]) {
int i,k=0;
for(i=0;i<pn;i++){
if(n==1)break;
if(prime[i]*prime[i]>n){
rest[k++]=n;
break;
}
while(n%prime[i]==0){
n/=prime[i];
rest[k++]=prime[i];
}
}
return k;
}
//任意进制转换
void conversion(char s[],char s2[],long d1,long d2) {
long i,j,t,num;
char c;
num=0;
for(int i=0;s[i]!='\0';i++){
if(s[i]<='9'&&s[i]>='0'){
t=s[i]-'0';
}
else{
t=s[i]-'A'+10;
num=num*d1+t;
}
}
i=0;
while(1){
t=num%2;
if(t<=9){
s2[i]=t+'0';
}
else{
s2[i]=t+'A'-10;
}
num/=d2;
if(num==0)break;
i++;
}
for(j=0;j<i/2;j++){
c=s2[j];s2[j]=s[i-j];s2[i-j]=c;
}
s2[i+1]='\0';
}
void solve(){
}
//字符串查找
int strfind(char str[],char key[]){
int l1,l2,i,j,flag;
l1=strlen(str);
l2=strlen(key);
for(i=0;o<=l1-l2;i++){
flag=1;
for(j=0;j<l2;j++){
if(str[i+j]!=key[j]){
flag=0;break;
}
}
if(flag)return i;
}
return -1;
}
//大数加法
string solve(string s,string t){
if(s.size()<t.size()){
string temp=s;
s=t;
t=temp;
}
int len1=s.size();
int len2=t.size();
int num1,num2,flag=0;sum;
while(len1>0){
num1=s[len1-1]-'0';
if(len2>0){
num2=t[len2-1]-'0';
}
else{
num2=0;
}
sum=num1+num2+flag;
s[len1-1]=sum%10+'0';
flag=sum/10;
len1--;
len2--;
}
if(flag==1){
s="1"+s;
}
return s;
}
以上内容部分来自/参考:博主Turing_Sheep、b站up别喷我id、博主渡梦酒
内容整理后加入了自己的解释和一些代码示例,看过一遍后建议自己动手敲一敲记得更牢。
一些蓝桥杯的感受:我个人转专业去计算机的,所以代码可以说是从0开始。我报的是大二四月份的蓝桥杯,现在已经打完过了几天了,蓝桥杯给我的感受就是——难!有的题有思路但单独敲代码自己敲不出来,有的题根本就看不懂,然后就被狠狠敲打了一波。
我一开始对蓝桥杯有想法,一是它知名度高,二是听说很好拿奖(前60%获奖),三是听说很水(可以各种方法骗分等)。但是!只有打过才知道蓝桥杯是人水赛不水的,会就是会不会就是不会,该考的算法会写就有分不会就没有。我是考前一个月才开始猛学算法,dfs、bfs、dp、贪心什么的都学了,题也敲了好几道,但都是需要看参考答案,自己能看懂理解思路但是单独敲不出来(也是我考场上有思路但敲不出来的原因,还是练得太少)。所以如果不是资深大佬不要轻视这个竞赛,谁轻视谁打完变clown。
竞赛学习路径的话可以去参照一些up或博主什么的,我是先学的c,把鹏哥的课几乎都看完了也都有上手敲,然后在考前一个月开始学算法并转了c++,然后考前一个星期去蓝桥云客猛刷了四五十道题,然后感觉还是打得很惨。但是不得不说这个竞赛真的让我产生了很大动力去学习和敲代码,正所谓以赛促学嘛,还是很值得参加的。