# **15届蓝桥杯总结**
对于本次蓝桥杯比赛,我认为我发挥得不是很好。
原因有三:
- 基础算法没有掌握好
- 没做好时间分配
- 一些知识点掌握得不牢
因此我打算对我之前学过的基础算法进行总结巩固练习。
------------
### 一些技巧上的总结
#### 数字与字符串之间的转换
使用c11版本的devc++,可以使用自带的库函数实现字符串转整数( stoi() ),代码如下:
```cpp
#include<bits/stdc++.h>
using namespace std;
int main(){
string s="1123456";
int a=stoi(s);
cout<<a<<endl;
return 0;
}
```

当然你还可以使用sscanf映射,具体语法如下:
使用sscanf将字符串转成整数,该字符串需要是char数组
```cpp
#include<bits/stdc++.h>
using namespace std;
int main(){
int a=0;
char s[7]="123456";
sscanf(s,"%d",&a);//将s从左到右以整数输入到a,注意需要 取址(&)符
cout<<"a="<<a<<endl;
return 0;
}
```

当然你还可以使用**stringstream**来实现,需要引用头文件: "sstream"
具体代码如下:
```cpp
#include<iostream>
#include<sstream>
using namespace std;
int main(){
string s="233333";
stringstream ss(s);
int a;
ss>>a;
cout<<a<<endl;
return 0;
}
```

#### **int转string**
前面我们提到sscanf可以将字符串转换成整数,那么sprintf()显然就可以将整数转换成字符串啦!之前sscanf是不是将左边的输入给右边,那么sprintf就是将右边的输入到左边咯,不同的是sprintf不需要&符(~~你见过谁家打印函数需要取址符的~~),示例代码如下:
```cpp
#include<bits/stdc++.h>
using namespace std;
int main(){
int a=2335468;
char tmp[10];
sprintf(tmp,"%d",a);
string s=tmp;
cout<<s<<endl;
return 0;
}
```

我们还可以使用itoa函数实现,调用include <stdlib.h>
具体语法:* char * itoa ( int value, char * str, int base ); 解释:value是我们要转化的数,str是用来接收的,后面的base是转化后的进制数 ,示例代码如下:
```cpp
#include<iostream>
#include<stdlib.h>
using namespace std;
int main(){
int value=123456;
char str[10];
itoa(value,str,10);
cout<<"str="<<str<<endl;
return 0;
}
```

**个人更加喜欢sscanf和sprintf,因为这两个函数适用性更广,你可以将%d改成%f或者其他的,这样你就可以实现浮点数与字符串的转换.**
当然还有to_string方法,学过Java的同学都知道to_string方法可以将各种数据类型转换成字符串,示例代码如下:
```cpp
#include<iostream>
#include<string>
using namespace std;
int main()
{
int a=123456;
double d=123.456;
long long l=1235465489;
string s1=to_string(a);
string s2=to_string(d);
string s3=to_string(l);
cout<<"s1="<<s1<<endl;
cout<<"s2="<<s2<<endl;
cout<<"s3="<<s3<<endl;
return 0;
}
```
#### 四舍五入
有时候题目会要求我们输出四舍五入的结果,因此我们需要知道这方面的知识。
一般题目要求无非分为两种:
**1.四舍五入只保留整数**
1)只需要输出就可以,那么这时候我们就可以使用printf函数实现这一功能了,示例代码:
```cpp
#include<bits/stdc++.h>
using namespace std;
int main(){
//只要求输出
double d=123.45;
printf("%.0lf",d);
return 0;
}
```

2)保留结果
直接调用cmath中的round函数
```cpp
#include<iostream>
#include<cmath>
using namespace std;
int main(){
//要求保留结果
double a=123.456;
double b=3456.2464161;
double c=25875.22235;
cout<<"round(a) = "<<round(a)<<endl;
cout<<"round(b) = "<<round(b)<<endl;
cout<<"round(c) = "<<round(c)<<endl;
return 0;
}
```

**2. 保留小数点后k位**
1)只要求输出
```cpp
#include<iostream>
#include<cmath>
using namespace std;
int main(){
//保留小数点后k位
double d=123.456;//小数点后有三位,我们保留两位,那么之后的就会往前补齐,但是如果在第2位后的第1位小于5,那么就算补齐还是不变的
printf("%.2lf",d);//保留小数点后2位
return 0;
}
```

2)要求保留结果
------------
## 基础算法
#### 模拟
#### 高精度
高精度一般是用于实现超出long long或者double表示范围的加法、减法、乘法和除法。基本原理就是:使用字符串模拟数字的计算,利用类似于数学里的竖式表示,一位一位进行计算的。
##### 高精度加法
主要的核心操作就是使用数组去模拟加法的进位,以及去除前导0
代码如下:
```cpp
#include<bits/stdc++.h>
using namespace std;
int a[505],b[505],c[555]; //a b数组为加数 和 被加数 ,c数组存进位和结果
int main(){
string s1,s2;
cin>>s1>>s2;
int l1=s1.size();
int l2=s2.size();
for(int i=0;i<l1;i++){
a[l1-i-1]=s1[i]-'0';//将操作数倒置 比如我们输入123 我们想从低位开始操作就必须倒置成321
}
for(int i=0;i<l2;i++){
b[l2-i-1]=s2[i]-'0';
}
int l3= max(l1,l2) + 1;//取l1和l2中最大的然后加1 因为加法有进位 比如12+88=100 加数和被加数都是两位 但是结果为3位
//运算
for(int i=0;i<l3;i++){
c[i]+=a[i]+b[i];//等同于 c[i] = a[i] + b[i] + c[i] ;
if(c[i] >= 10 ){//如果 加完之后>=10,就需要进位
c[i+1] += c[i]/10;//i的下一位 + 第i位/10
c[i]=c[i]%10;//留下进位之后剩余的数
}
}
while(c[l3-1] ==0 && l3>1 ) l3--;//去掉前导0 并且保证留最少1位 因为如果是 0 + 0 = 0 ,他的结果只有1个数(0) ,所以不能去掉
for(int i=l3-1;i>=0;i--) cout<<c[i];//打印结果
return 0;
}
```
大家可以应用一下,这是一道洛谷上的高精度加法模版题:[高精度加法模版题](https://www.luogu.com.cn/problem/P1601 "高精度加法模版题")
##### 高精度减法
高精度加法需要进位,那么减法当然需要借位啦,同样滴,减法也需要注意去除前导0 , 代码如下:
```cpp
#include<bits/stdc++.h>
using namespace std;
int a[10087],b[10087],c[10087];
bool cmp(string s1,string s2){//比较减数与被减数的大小 只需要需要字符串的长度即可 如果s1的长度大于s2 那么
if(s1.length() < s2.length() ) return true;//如果s1的长度小于s2 那么需要交换 返回true
if(s1.length() == s2.length() && s1<s2 ) return true;//如果s1与s2长度相等但是 s1>s2 也需要交换 返回true
return false;//上述均不满足 就返回false
}
int main(){
string s1,s2;
int flag=0;//flag变量用于表示是否需要输出负号
cin>>s1>>s2;
if(cmp(s1,s2)){
flag=1;//如果需要交换 那么说明s1小于s2 相当于 1 - 2 所以结果是负数 需要输出负号
swap(s1,s2);//交换;两个数 原因 1 - 5 运算 是不是相当于 5 - 1 然后结果加一个负号
}
int l1=s1.size();
int l2=s2.size();
for(int i=0;i<l1;i++) a[l1-i-1]=s1[i]-'0';//数组倒置 与高精度加法一样
for(int i=0;i<l2;i++) b[l2-i-1]=s2[i]-'0';
int l3=max(l1,l2);//减法不需要 长度+1
for(int i=0;i<l3;i++){
if(a[i] < b[i]){//如果当前数 小于 被减数 说明需要借位 那么 i+1 位 减 1 i位加10
a[i+1]--;
a[i] += 10;
}
c[i]=a[i]-b[i];//执行减法
}
while(c[l3-1] ==0 && l3>1 ) l3--;//与高精度加法一样 去除前导0
if(flag) cout<<"-";//如果结果为负数 输出负号
for(int i=l3-1;i>=0;i--) cout<<c[i];//输出结果
return 0;
}
```
同样,来练一下手吧!题目链接:[高精度减法模版题](https://www.luogu.com.cn/problem/P2142 "高精度减法模版题")
ok,接下来就轮到高精度乘法和高精度除法啦!!!
##### 高精度乘法
高精度乘法分为高精度乘以高精度、高精度乘以低精度
##### 高精度乘以低精度
高精度乘以低精度我们只需要对一个数进行字符串的模拟操作即可(对大数进行操作),其核心的操作是逐位相乘,处理进位,对于乘法和除法除了考虑去除前导0的操作之外,还需要判断进位是否会导致长度l增加,代码如下:
```cpp
#include<bits/stdc++.h>
using namespace std;
int a[505],b[505],c[505];
int main(){
string s1;
int d;
cin>>s1>>d;
int l=s1.size();
for(int i=0;i<l;i++) a[l-i-1]=s1[i]-'0';
for(int i=0;i<l;i++){
c[i] += a[i]*d;//等同于 c[i]=c[i] + a[i]*d
if(c[i] >= 10){//处理进位
c[i+1] += c[i]/10;
c[i] = c[i] % 10;
}
}
if(c[l] > 0) l++;//判断是否需要进位到c[l]位置
while(c[l-1] == 0 && l > 1) l--;
for(int i=l-1;i >= 0;i--) cout<<c[i];
return 0;
}
```
##### 高精度乘以高精度
高精度乘以高精度的话,相比于高乘以低,我们这里需要两个for循环嵌套来模拟乘法每一位对应相乘和移位,代码如下:
```cpp
#include<bits/stdc++.h>
using namespace std;
int a[200],b[200],c[500];
int main()
{
string s1,s2;
cin>>s1>>s2;
int l1=s1.size();
for(int i=0;i<l1;i++) a[l1-i-1]=s1[i]-'0';
int l2=s2.size();
for(int i=0;i<l2;i++) b[l2-i-1]=s2[i]-'0';
for(int i=0;i<l1;i++){
for(int j=0;j<l2;j++){
int k=i+j;
c[k] += a[i]*b[j];
if(c[k]>=10){
c[k+1] += c[k]/10;
c[k]=c[k]%10;
}
}
}
int l=l1+l2+1;
if(c[l] > 0) l++;
while(c[l-1]==0&&l>1) l--;
for(int i=l-1;i>=0;i--) cout<<c[i];
return 0;
}
```
老规矩,模版题传送门:[高精度乘法(高精度乘高精度)模版题](https://www.luogu.com.cn/problem/P1303 "高精度乘法(高精度乘高精度)模版题")
------------
#### 前缀和与差分
前缀和一般应用在题目要求你求:
1. 子数组和(比如:最大子数组和或最小子数组和)
2. 区间和
3.数组元素的更新
前缀和分为一维前缀和和二维前缀和
一维前缀和:sum[i] = a[i] + sum[i];
就是 当前sum数组的第 i 位 等于 数组a的第i位元素 + sum数组的第 i-1 位。
示例代码:
```cpp
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int a[N],sum[N];//a数组存原本的元素 , sum数组构造前缀和
int main(){
int n;
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i];
for(int i=1;i<=n;i++) sum[i] = a[i] + sum[i-1] ;// 核心
int q;
cin>>q;//q次询问
while(q--){
int l,r;
cin>>l>>r;//输入区间的左右范围
cout<<sum[r]-sum[l-1]<<endl;
}
return 0;
}
```
模版题:[一维前缀和模版题](https://www.luogu.com.cn/problem/B3612 "一维前缀和模版题")
------------
#### 构造
------------
#### 二分答案法
------------
#### 排序
------------
#### 分治
------------
#### 贪心
------------