1. C++基础语法与STL容器实战指南
作为一名参加过多次蓝桥杯竞赛的老选手,我深知在算法竞赛中熟练掌握C++基础语法和STL容器的重要性。本文将系统梳理竞赛中最常用的语法特性和容器操作,并分享一些实战中的经验技巧。
1.1 变量与基础数据结构
在算法竞赛中,变量声明看似简单却暗藏玄机。比如整型变量的选择就很有讲究:
cpp复制int a = 5; // 32位有符号整数,范围-2^31~2^31-1
long long b = 1e18; // 64位有符号整数,处理大数必备
unsigned int c = 1; // 无符号整型,位运算时常用
特别注意:蓝桥杯竞赛中经常出现1e18级别的大数,务必养成使用long long的习惯,避免整数溢出。
数组作为最基础的数据结构,在竞赛中有多种实现方式:
cpp复制// 静态数组 - 适合已知最大规模的场景
const int MAXN = 1e5+5;
int arr[MAXN];
// 动态数组 - 更灵活的size控制
vector<int> v;
v.reserve(100); // 预分配空间避免频繁扩容
1.2 流程控制优化技巧
循环和条件语句虽然是基础,但在竞赛中需要特别注意效率:
cpp复制// 传统for循环 - 明确控制迭代次数
for(int i=0; i<n; ++i) {
// 前置++比后置++效率更高
}
// 范围for循环 - 简化容器遍历
for(auto& x : v) {
// 使用引用避免拷贝开销
}
// while循环 - 适合不确定迭代次数的场景
while(condition) {
// 注意设置终止条件防止死循环
}
条件语句的优化技巧:
- 将最可能成立的条件放在前面
- 使用switch替代多重if-else
- 避免在循环内部进行复杂条件判断
2. STL容器深度解析与应用
2.1 序列式容器实战
vector动态数组
vector是竞赛中最常用的容器,其底层实现是动态分配的数组:
cpp复制vector<int> v = {1,2,3};
v.emplace_back(4); // 比push_back更高效
v.shrink_to_fit(); // 释放多余内存
v.data(); // 获取底层数组指针
性能提示:vector的push_back操作平均时间复杂度是O(1),但在扩容时需要O(n)时间复制元素。对于已知大小的vector,建议先用reserve预分配空间。
string字符串处理
string比C风格字符串更安全高效:
cpp复制string s = "hello";
s.append(" world", 3); // 只追加前3个字符
s.compare("hello"); // 字符串比较
s.replace(2,3,"xxx"); // 从位置2开始替换3个字符
2.2 关联式容器精讲
set有序集合
set基于红黑树实现,元素自动排序:
cpp复制set<int> s = {3,1,4};
auto it = s.lower_bound(2); // 第一个≥2的元素
s.emplace_hint(it, 2); // 提示插入位置提高效率
map键值对映射
map的operator[]有自动插入特性:
cpp复制map<string, int> m;
m["key"]++; // 如果key不存在会自动插入0
m.at("key"); // 不存在会抛出异常
m.count("key"); // 判断是否存在
2.3 容器适配器应用
priority_queue优先队列
默认是大根堆,常用于Dijkstra等算法:
cpp复制// 小根堆实现方式
priority_queue<int, vector<int>, greater<int>> min_heap;
// 自定义比较函数
auto cmp = [](pair<int,int> a, pair<int,int> b) {
return a.second > b.second;
};
priority_queue<pair<int,int>, vector<pair<int,int>>, decltype(cmp)> pq(cmp);
3. STL算法实战技巧
3.1 排序与查找算法
sort函数是竞赛中最常用的算法之一:
cpp复制vector<int> v = {3,1,4,2};
sort(v.begin(), v.end()); // 默认升序
sort(v.rbegin(), v.rend()); // 降序排序技巧
// 自定义排序规则
sort(v.begin(), v.end(), [](int a, int b) {
return abs(a) < abs(b); // 按绝对值排序
});
二分查找相关算法:
cpp复制auto it = lower_bound(v.begin(), v.end(), 3); // 第一个≥3的位置
bool found = binary_search(v.begin(), v.end(), 3); // 是否存在
3.2 数值与排列操作
数学相关算法:
cpp复制int a = 10, b = 20;
swap(a, b); // 交换变量
int g = __gcd(a, b); // GCC内置gcd函数
bool p = is_permutation(v.begin(), v.end(), u.begin()); // 判断排列
排列生成算法:
cpp复制vector<int> v = {1,2,3};
do {
// 处理当前排列
} while(next_permutation(v.begin(), v.end()));
4. 数论算法实现
4.1 最大公约数与最小公倍数
欧几里得算法及其优化:
cpp复制// 递归实现
int gcd(int a, int b) {
return b ? gcd(b, a%b) : a;
}
// 迭代实现(效率更高)
int gcd_iter(int a, int b) {
while(b) {
a %= b;
swap(a, b);
}
return a;
}
// 最小公倍数计算
int lcm(int a, int b) {
return a / gcd(a, b) * b; // 先除后乘防溢出
}
4.2 素数筛法比较
埃拉托斯特尼筛法优化版:
cpp复制void eratosthenes(int n) {
vector<bool> is_prime(n+1, true);
is_prime[0] = is_prime[1] = false;
for(int i=2; i*i<=n; ++i) {
if(is_prime[i]) {
// 从i*i开始标记,i*(i-1)已被标记过
for(int j=i*i; j<=n; j+=i) {
is_prime[j] = false;
}
}
}
}
欧拉线性筛法实现:
cpp复制void euler_sieve(int n) {
vector<int> primes;
vector<bool> is_prime(n+1, true);
for(int i=2; i<=n; ++i) {
if(is_prime[i]) primes.push_back(i);
for(int j=0; j<primes.size() && i*primes[j]<=n; ++j) {
is_prime[i*primes[j]] = false;
if(i % primes[j] == 0) break; // 关键优化
}
}
}
筛法选择建议:当n≤1e7时欧拉筛更优,n>1e7时埃氏筛更节省内存。
5. 进制转换与位运算
5.1 进制转换实现
k进制转十进制通用模板:
cpp复制int kToDecimal(string s, int k) {
int res = 0;
for(char c : s) {
int digit = isdigit(c) ? c-'0' : c-'A'+10;
res = res * k + digit;
}
return res;
}
十进制转k进制递归实现:
cpp复制string decimalToK(int n, int k) {
if(n == 0) return "0";
string digits = "0123456789ABCDEF";
string res;
while(n > 0) {
res = digits[n%k] + res;
n /= k;
}
return res;
}
5.2 位运算技巧
常用位操作技巧:
cpp复制// 判断奇偶
bool isOdd = x & 1;
// 取最低位的1
int lowbit = x & -x;
// 统计1的个数
int cnt = __builtin_popcount(x);
// 二进制枚举子集
for(int mask=1; mask<(1<<n); ++mask) {
// 处理当前子集
}
6. 竞赛实用模板
6.1 头文件与宏定义
标准竞赛模板头:
cpp复制#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
const int INF = 0x3f3f3f3f;
const ll LINF = 0x3f3f3f3f3f3f3f3f;
const int MOD = 1e9+7;
#define FAST_IO ios::sync_with_stdio(false);cin.tie(nullptr)
#define debug(x) cerr << #x << " = " << x << endl
#define rep(i, a, b) for(int i = (a); i <= (b); i++)
#define per(i, a, b) for(int i = (a); i >= (b); i--)
6.2 输入输出优化
快速IO实现:
cpp复制void fastIO() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
}
// 快速读取整数
inline int read() {
int x = 0, f = 1;
char ch = getchar();
while(ch < '0' || ch > '9') {
if(ch == '-') f = -1;
ch = getchar();
}
while(ch >= '0' && ch <= '9') {
x = x * 10 + ch - '0';
ch = getchar();
}
return x * f;
}
在实际竞赛中,我发现很多选手容易忽视基础知识的系统整理,导致在关键时刻出现低级错误。建议定期复习这些基础内容,并建立自己的代码片段库。对于STL容器,不仅要会用,还要了解其底层实现原理和时间复杂度,这样才能在复杂场景下做出最优选择。