蓝桥杯 C++ AB 组辅导课
教学计划与递归
如何准备蓝桥杯比赛
-
要有一定刷题量
若想在省赛中取得较好成绩,建议做200题左右。
若想在国赛中取得较好成绩,建议做300题左右。
本课程约讲解100题左右。 -
要重视调试
有思路得前提下,若调试不过,不要立马问别人。要自己努力解决问题。 -
要重视考试。
感受真实氛围,检验学习成果。 -
比赛时比的并不是去发明或者去创造某一个思路,比赛比的是熟练度。
做题的套路
-
算法题特点:
1、与数学、物理等学科相比,算法题的模型相对较少。
2、思维量高
3、编写编程能力 。要用理论去指导实践。数学题想出来就能做出来,但是算法题能想出来不一定能实现出来。 -
做题过程
1、看题目描述
2、分析,抽象出来它考什么模型。分析出题人是想问什么。
3、回忆之前学过的算法或做过的题目能不能解决这个问题。若不能,再去回溯。解决方案是平时多去积累一些题目,多去刷一些题目。只有平时积累的多了,考试的时候才能学以致用。 -
如何判断一个算法能不能用?
1、首先看正确性
2、再看时间是否超限制。
C++ 评测机一秒约运行一亿次。若算法的的时间复杂度超过108,则可能会超时。
做题时,先看数据范围,排除不能使用的算法。找到题目数据范围区间之内的算法。这样做题思路会更明确。
数据范围可以带给我们非常多的信息。 -
学算法一定要落实到代码上
-
若输入输出 范围 小于 105 推荐用 cin / cout 否则用 scanf / printf 输入时间会快一倍左右。
第一讲 递归与递推
- 递归是什么?递归就是自己调用自己。
递归计算斐波那契数列
#include<iostream>
using namespace std;
int f(int n )
{
if (n == 1) return 1;
if (n == 2) return 2;
return f(n - 1) + f(n - 2);
}
int main()
{
int n ;
cin >> n;
cout << f(n);
return 0;
}
- 如何分析判断 递归执行顺序
所有递归都可以转化为一颗递归搜索树
- 记忆几个比较常用的数据
21 = 2
22 = 4
23 = 8
24 = 16
25 = 32
26 = 64
27 = 128
28 = 256
29 = 512
210 = 1024
215 = 32768
216 = 65536
220 约等于106
263 约等于 108
递归实现指数型枚举
#include<iostream>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int N = 16;
int n;
int st[N];//状态,记录每个位置当前的状态:0表示还没考虑,1表示选它,2表示不选它。
void dfs(int u)
{
if (u > n)
{
for (int i =1; i <= n; i ++ )
if (st[i] == 1)
printf("%d ", i);
printf("\n");
return;
}
st[u] = 2;
dfs(u + 1); //第一个分支: 不选
st[u] = 0; //恢复现场
st[u] = 1;
dfs(u + 1); // 第二个分支: 选
st[u] = 0;
}
int main()
{
cin >> n;
dfs(1);
return 0;
}
变形 :如何输出所有方案?
#include<iostream>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
const int N = 16;
int n;
int st[N];//状态,记录每个位置当前的状态:0表示还没考虑,1表示选它,2表示不选它。
vector<vector<int>> ways;
void dfs(int u)
{
if (u > n)
{
vector<int> way;
for (int i = 1; i <= n; i ++ ) // 记录方案
if (st[i] == 1)
way.push_back(i);
ways.push_back(way);
return;
}
st[u] = 2;
dfs(u + 1); //第一个分支: 不选
st[u] = 0; //恢复现场
st[u] = 1;
dfs(u + 1); // 第二个分支: 选
st[u] = 0;
}
int main()
{
cin >> n;
dfs(1);
for (int i = 0; i < ways.size(); i ++ )
{
for (int j = 0; j < ways[i].size(); j ++) printf("%d ", ways[i][j]);
puts(""); // puts是输出一个字符串,但是这个字符串为空,仅有一个回车。
//相当于printf("\n"); puts("")等价于只输出回车。
}
return 0;
}
递归实现排列型枚举
- 有两种顺序
1、依次枚举每个数放到每个位置
2、依次枚举每个位置放哪个数
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int N = 10;
int n;
int state[N]; // 0表示还没放数,1~n表示放了哪个数
bool used[N]; // true表示用过,false表示还未用过
void dfs(int u)
{
if (u > n)
{
for (int i = 1; i <= n; i ++ )
cout << state[i] << ' ';
cout << endl;
return;
}
for (int i = 1; i <= n; i ++)
if (!used[i])
{
state[u] = i;
used[i] = true;
dfs(u + 1);
state[u] = 0;
used[i] = false;
}
}
int main()
{
cin >> n;
dfs(1);
return 0;
}
递归实现组合型枚举
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int N = 30;
int n, m;
int way[N];
void dfs(int u, int start)
{
if (n + u - start < m) return; // 如果把后面所以的数都选上还不够m个 当前分支无解。
if (u == m + 1)
{
for (int i = 1; i <= m ; i ++) printf("%d ", way[i]);
puts("");
return;
}
for (int i = start; i <= n; i ++)
{
way[u] = i;
dfs(u + 1, i + 1);
way[u] = 0;
}
}
int main()
{
scanf("%d%d", &n, &m);
dfs(1, 1);
return 0;
}
AcWing116.飞行员兄弟
第二讲 二分与前缀和
AcWing789.数的范围
#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
using namespace std;
const int N = 100010;
int n ,m;
int q[N];
int main()
{
cin >> n >> m;
for (int i = 0; i < n; i ++) cin >> q[i];
for (int i = 0; i < m; i ++)
{
int x;
cin >> x;
int l = 0, r = n - 1;
while (l < r)
{
int mid = l + r >> 1;
if (q[mid] >= x) r = mid;
else l = mid + 1;
}
if (q[r] == x) // 比较的是数组里的元素
{
cout << r << ' ';
r = n - 1;
while (l < r)
{
int mid = l + r + 1 >> 1;
if (q[mid] <= x) l = mid; //**这里写mid是不对的,要写[mid]
else r = mid - 1;
}
cout << l << endl;
}
else cout << "-1 -1" << endl;
}
return 0;
}
AcWing790.数的三次方根
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
int main()
{
double x;
cin >> x;
double l = -100000, r = 100000;
while (r - l > 1e-8)
{
double mid = (l + r) / 2;
if (mid * mid * mid >= x) r = mid;
else l = mid;
}
printf("%.6lf", l);
return 0;
}
AcWing795.前缀和
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 100010;
int n, m;
int a[N];
int s[N];
int main()
{
cin >> n >> m;
for (int i = 1; i <= n; i ++)
{
cin >> a[i];
s[i] = s[i - 1] + a[i];
}
while (m --)
{
int l, r;
cin >> l >> r;
cout << s[r] - s[l - 1] << endl;
}
return 0;
}
AcWing796.子矩阵的和
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 1010;
int n, m, q;
int a[N][N], s[N][N];
int main()
{
cin >> n >> m >> q;
for (int i = 1; i <= n; i ++)
for (int j = 1; j <= m; j ++)
{
cin >> a[i][j];
s[i][j] = s[i - 1][j] + s[i][j - 1] - s[i - 1][j - 1] + a[i][j];//++-+
}
while (q --)
{
int x1, y1, x2, y2;
cin >> x1 >> y1 >> x2 >> y2;
cout << s[x2][y2] - s[x1 - 1][y2] - s[x2][y1 - 1] + s[x1 - 1][y1 - 1] << endl;//+--+
}
return 0;
}
#include <iostream>
using namespace std;
const int N = 10010;
int n, m, q;
int a[N][N];
int s[N][N];
int main()
{
cin >> n >> m >> q;
for (int i = 1; i <= n ; i++)
for (int j = 1; j <= m; j ++)
{
cin >> a[i][j];
s[i][j] = s[i - 1][j] + s[i][j - 1] - s[i - 1][j - 1] + a[i