点关注不迷路,欢迎推荐给更多人,大约两天更新一次,建议点赞收藏加关注
本次更新内容:1.STL部分详细讲述,放到一篇新的文章中
2. 部分细节优化
目录
1.1 取消同步(节约时间,甚至能多骗点分,最好每个程序都写上)
1 技巧
1.1 取消同步(节约时间,甚至能多骗点分,最好每个程序都写上)
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
1.2 万能库(可能会耽误编译时间,但是省脑子)
#include <bits/stdc++.h>
1.3 蓝桥杯return 0千万别忘了写!!
1.4 编译设置(Dev C++)
(1)工具->编译选项->编译器->编译时加入以下命令->调成C99
(2)工具->编译选项->代码生成/优化->代码生成->语言标准
1.5 memset填充函数
按照字节对内存块进行初始化,注意只能填充0或-1
#include <bits/stdc++.h>
using namespace std;
int a[10];
int main()
{
memset(a,-1,sizeof(a));
for(int i=0;i<10;i++)
{
cout<<a[i]<<endl;
}
return 0;
}
1.6 时间复杂度
蓝桥杯每一道题编译时间都限制在1s以内,遇到数据比较大的题目,往往需要降低时间复杂度。
粗略估计,O(n)情况下一秒大概完成4亿次,O(n*n)情况下一秒大概完成2万次,O(n*n*n)情况下大概完成700次。
由于蓝桥杯评测系统是根据通过样例数来评分,所以我们做题时一定要注意约定样例取值范围。
例题:K倍区间(暴力法只能通过部分样例,所以要用更好的算法)
1.6.1 常数阶 O(1)
int i=1;
int j=2;
int m=i+j;
1.6.2 对数阶 O(logn)
int i=1;
while(i<n)
{
i=i*2;
}
1.6.3 线性阶 O(n)
for(int i=0;i<n;i++)
{
cout<<i<<endl;
)
1.6.4 线性对数阶 O(nlogn)
for(int m=1;m<n;m++)
{
int i=1;
while(i<n)
{
i=i*2;
}
}
1.6.5 多重循环 O(n^k)
k为循环层数
1.7 剪枝
做题时已经发现的不可能取到的数值,就不要再让计算机算了,尽量节省时间,蓝桥杯中目前遇到的还没有用到过过于繁琐的剪枝,大多也是在BFS和DFS中出现(bool vis)
1.8 find函数
函数作用:查找该元素在数组中第一次出现的位置的地址(类似于0x的地址)
模型:find(查找起点,查找终点,查找目标元素)
同样,查找区间为[查找起点,查找终点)
#include <bits/stdc++.h>
using namespace std;
int main()
{
int a[10]={2,6,8,1,3,7,5,1,0,11};
cout<<find(a+0,a+5,8)<<endl;//打印类似0x地址
cout<<find(a+0,a+5,8)-a<<endl;//打印数组【】内的序号
return 0;
}
1.9 PI问题
PI=atan(1.0)*4
PI=3.14159265
1.10 C/C++帮助文档
1.11 最大空间
1.11.1 占用字节大小
类型 | 32位计算机 | 64位计算机 |
char | 1 | 1 |
short int | 2 | 2 |
int | 4 | 4 |
long int | 4 | 8 |
long long int | 8 | 8 |
char* | 4 | 8 |
float | 4 | 4 |
double | 8 | 8 |
1.11.2 常用数据范围
类型 | 范围 | 估计值 |
char | -128~+127 | |
short | -32767~+32768 | 3*10^4 |
unsigned short | 0~+65536 | 6*10^4 |
int=long | -2147483648~+2147483647 | 2*10^9 |
unsigned int | 0~+4294967295 | 4*10^9 |
long long | -9223372036854775808~+9223372036854775807 | 9*10^18 |
1.12 指针存字符串(了解)
这个比赛很少用指针,如果想要存储字符串,用指针也是一个不错的选择(直接可以用string类避免用指针)
//指针数组存储字符串列表
#include <stdio.h>
const int max = 5;
int main()
{
const char *names[] =
{ //定义指针数组,存储字符串列表
"Zhang San", //每个元素都是双引号括起来的
"Li Si",
"Wang Wu",
"Su Wukong",
"Tang Er",
};
int i=0;
for(i=0;i<max;i++)
{
printf("Value of names[%d] = %s\n",i,names[i]); // 注意 %s
}
return 0;
}
2 算法+数据结构
2.1 BFS(宽度优先搜索)
用到队列(有时会用到优先队列)
主要思想:把所有符合条件的点都压入队列,然后挨个元素弹出上下左右前后搜索,直到队列清空时代表搜索完毕,搜索的时候注意判断是否已经搜索过,用bool vis【】判断。
例题:全球变暖
2.2 DFS(深度优先搜索)
用到递归(不好理解)
主要模板:可参见如下全排列例题
总结起来有如下几步:
(1)确定 边界 if()return;
(2)进入for循环
(3)判断是否搜索过 if(vis[])vis[]=true; dfs(); vis[]=false;
例题1:凑算式
例题2:2n皇后问题
2.3 最大公约数和最小公倍数
最大公约数(greatest common divisor,gcd)
#include <bits/stdc++.h>
using namespace std;
int main()
{
cout<<__gcd(25,5);
return 0;
}
最小公倍数 (least common multiple,lcm)
多写一个lcm函数
#include <bits/stdc++.h>
using namespace std;
int lcm(int a,int b)
{
return a*b/__gcd(a,b);
}
int main()
{
cout<<lcm(25,5);
return 0;
}
2.4 进制转换+超大数据处理
2.4.1 十进制为媒介(常用型)
2.4.2 二进制为媒介(技巧型)(含超大数据处理)
2.5 二进制表示法
例题:无聊的逗
2.6 背包问题
2.6.1 01背包问题
#include<bits/stdc++.h>
using namespace std;
int v[1000]; // 体积
int w[1000]; // 价值
int f[1000][1000]; // f[i][j], j体积下前i个物品的最大价值
int main()
{
int n, m;
cin >> n >> m;
for(int i = 1; i <= n; i++)
cin >> v[i] >> w[i];
for(int i = 1; i <= n; i++)
for(int j = 1; j <= m; j++)
{
// 当前背包容量装不进第i个物品,则价值等于前i-1个物品
if(j < v[i])
f[i][j] = f[i - 1][j];
// 能装,需进行决策是否选择第i个物品
else
f[i][j] = max(f[i - 1][j], f[i - 1][j - v[i]] + w[i]);
}
cout << f[n][m] << endl;
return 0;
}
2.6.2 多重背包问题(每种物品有多件)
把多件物品捏合成一件新的物品,按序号往后叠加即可
2.6.3 完全背包问题(每种物品有无数件)
#include<iostream>
using namespace std;
const int N = 1010;
int f[N];
int v[N],w[N];
int main()
{
int n,m;
cin>>n>>m;
for(int i = 1 ; i <= n ;i ++)
{
cin>>v[i]>>w[i];
}
for(int i = 1 ; i<=n ;i++)
for(int j = v[i] ; j<=m ;j++)
{
f[j] = max(f[j],f[j-v[i]]+w[i]);
}
cout<<f[m]<<endl;
}
2.7 动态规划(DP)
例题1:拿金币
例题2:包子凑数
2.8 贪心
思路:选取局部最优解,但是最大的缺陷就是在某些情况下不适用
举例:纸币问题
比如面额有1元,2元,5元,10元,20元,50元,100元,那么对于110元来说,可以用贪心从最大面额100元开始找。
但是如果改纸币面额,比如1元,2元,5元,20元,55元,100元,那么如果用到贪心算法,会发现并不能找出最优解(贪心:100+5+5=110 动态规划:55+55=110)
动态规划代码如下:
#include <bits/stdc++.h>
using namespace std;
int dp[10000];
int n;
int b[6]={1,5,20,50,55,100};
int temp;
int main()
{
dp[0]=0;
dp[1]=1;
dp[5]=1;
dp[20]=1;
dp[50]=1;
dp[55]=1;
dp[100]=1;
for(int i=1;i<=10000;i++)
{
temp=10000;
for(int k=0;k<6;k++)
{
if(i>=b[k])
{
temp=min(dp[i-b[k]]+1,temp);
}
}
dp[i]=temp;
}
for(int i=1;i<10000;i++)
{
cout<<i<<"元用"<<dp[i]<<"张"<<endl;
}
return 0;
}
2.9 分治(以后更新)
大部分是二分法
2.10 数字分拆到数组中
2.11 数字和字符串的互化
求不了子数字,但能求子字符串
例题:超级质数
2.12 排序
2.13 冒泡排序法和二分查找法(最常用)
2.14 图论
2.15 常用树
2.16 快速幂算法
2.17 素数(质数)
质数(Prime number,又称素数),指在大于1的自然数中,除了1和该数自身外,无法被其他自然数整除的数(也可定义为只有1与该数本身两个正因数的数)
2.17.1 暴力法
两层循环,第一层循环i遍历大于等于2的所有数,第二层循环遍历大于1小于i的所有数j,判断i%j!=0一直成立,则该数为质数
这种方法虽然思路简单,但时间复杂度有时不能满足题目要求
2.17.2 线性筛
定理:一个质数*任何一个不为1的正整数=合数
有了定理之后,就可以通过定理优化代码降低时间复杂度,下面是具体思路
Eratosthenes筛选方法(Eratosthenes Sieve Method)
假设有一个筛子存放1~N,例如:
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 .........N
先将 2的倍数 筛去:
2 3 5 7 9 11 13 15 17 19 21..........N
再将 3的倍数 筛去:
2 3 5 7 11 13 17 19..........N
之后,再将5的倍数筛去,再来将7的质数筛去,再来将11的倍数筛去........,如此进行到最后留下的数就都是质数,这就是Eratosthenes筛选方法(Eratosthenes Sieve Method)。
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int primes[1000000];
int cnt;
bool notprime[1000000];
void set_prime()
{
notprime[1]=true;
primes[0]=0;
for(int i=2;i<1000000;i++)
{
if(!notprime[i])
{
primes[++primes[0]]=i;
}
for(ll j=(ll)i*i;j<1000000;j+=i)//保证每一个遍历的值都没被筛过
{
notprime[j]=true;
}
}
}
int main()
{
set_prime();
for(int i=1;i<=primes[0];i++)
{
cout<<primes[i]<<endl;
}
return 0;
}
2.18 递归
是指函数/过程/子程序在运行过程序中直接或间接调用自身而产生的重入现象。
在计算机编程里,递归指的是一个过程:函数不断引用自身,直到引用的对象已知。
使用递归解决问题,思路清晰,代码少。但是在主流高级语言中(如C语言、Pascal语言等)使用递归算法要耗用更多的栈空间,所以在堆栈尺寸受限制时(如嵌入式系统或者内核态编程),应避免采用。所有的递归算法都可以改写成与之等价的非递归算法。
递归的重点是找到递归表达式
例1:阶乘
#include <bits/stdc++.h>
using namespace std;
int f(int n)
{
if(n==1)
{
return 1;
}
return n*f(n-1);
}
int main()
{
cout<<f(10);
return 0;
}
例2:汉诺塔
2.19 STL
这里大概列出参加蓝桥杯需要掌握的知识点和技巧,若想详细了解某个知识点,可以看看我的例题和别人的博文。