5086 字
25 分钟
OI竞赛C++语法详解
OI竞赛C++语法详解
在信息学奥林匹克竞赛(OI)中,C++凭借其强大的标准模板库(STL)和高效的性能成为最受欢迎的编程语言。本文将全面讲解OI竞赛中必备的C++语法知识,从基础语法到高级技巧,助你在竞赛中游刃有余。
1. 基础语法
1.1 变量和数据类型
C++提供了丰富的数据类型,在OI竞赛中选择合适的数据类型至关重要。
基本数据类型
#include <iostream>using namespace std;
int main() { // 整型 int a = 100; // 4字节,范围约 -2×10^9 ~ 2×10^9 long long b = 1e18; // 8字节,范围约 -9×10^18 ~ 9×10^18
// 浮点型 double c = 3.14159; // 8字节,15-16位有效数字
// 字符型 char ch = 'A'; // 1字节
// 布尔型 bool flag = true; // true或false
// 无符号整型(只能存储非负数,范围翻倍) unsigned int d = 4e9; unsigned long long e = 1e19;
cout << "int: " << a << endl; cout << "long long: " << b << endl; cout << "double: " << c << endl; cout << "char: " << ch << endl; cout << "bool: " << flag << endl;
return 0;}OI应用场景:
- 当数据范围在 以内时使用
int - 当数据范围在 以内时使用
long long - 涉及坐标、概率、几何问题时使用
double - 注意:数组开得过大会导致栈溢出,建议全局变量或使用
vector
1.2 运算符
#include <iostream>using namespace std;
int main() { int a = 10, b = 3;
// 算术运算符 cout << "加法: " << a + b << endl; // 13 cout << "减法: " << a - b << endl; // 7 cout << "乘法: " << a * b << endl; // 30 cout << "除法: " << a / b << endl; // 3(整除) cout << "取模: " << a % b << endl; // 1
// 自增自减 int x = 5; cout << "x++: " << x++ << endl; // 输出5,x变为6 cout << "++x: " << ++x << endl; // x变为7,输出7
// 比较运算符 cout << "a > b: " << (a > b) << endl; // 1(true) cout << "a == b: " << (a == b) << endl; // 0(false)
// 逻辑运算符 bool p = true, q = false; cout << "p && q: " << (p && q) << endl; // 0 cout << "p || q: " << (p || q) << endl; // 1 cout << "!p: " << !p << endl; // 0
// 位运算符(竞赛中常用) int m = 5, n = 3; // 二进制:101 和 011 cout << "按位与: " << (m & n) << endl; // 1(001) cout << "按位或: " << (m | n) << endl; // 7(111) cout << "按位异或: " << (m ^ n) << endl; // 6(110) cout << "左移: " << (m << 1) << endl; // 10(1010) cout << "右移: " << (m >> 1) << endl; // 2(10)
return 0;}位运算技巧:
x & 1:判断奇偶性(等价于x % 2)x << 1:乘以2(等价于x * 2)x >> 1:除以2(等价于x / 2)x & (x - 1):去掉最低位的1,用于判断2的幂或统计1的个数x ^ x:结果为0,用于异或的性质
2. 控制结构
2.1 条件语句
if-else语句
#include <iostream>using namespace std;
int main() { int score; cin >> score;
// 简单if-else if (score >= 60) { cout << "及格" << endl; } else { cout << "不及格" << endl; }
// 多分支if-else if-else if (score >= 90) { cout << "优秀" << endl; } else if (score >= 80) { cout << "良好" << endl; } else if (score >= 70) { cout << "中等" << endl; } else if (score >= 60) { cout << "及格" << endl; } else { cout << "不及格" << endl; }
// 三元运算符(条件表达式) string result = (score >= 60) ? "Pass" : "Fail"; cout << result << endl;
return 0;}switch语句
#include <iostream>using namespace std;
int main() { int choice; cin >> choice;
switch (choice) { case 1: cout << "选项1" << endl; break; // break很重要,防止穿透 case 2: cout << "选项2" << endl; break; case 3: cout << "选项3" << endl; break; default: cout << "无效选项" << endl; }
return 0;}2.2 循环结构
for循环
#include <iostream>using namespace std;
int main() { // 基本for循环 for (int i = 1; i <= 10; i++) { cout << i << " "; } cout << endl;
// 倒序循环 for (int i = 10; i >= 1; i--) { cout << i << " "; } cout << endl;
// 步长为2 for (int i = 0; i < 20; i += 2) { cout << i << " "; } cout << endl;
// 嵌套循环(打印九九乘法表) for (int i = 1; i <= 9; i++) { for (int j = 1; j <= i; j++) { cout << j << "x" << i << "=" << i*j << "\t"; } cout << endl; }
return 0;}while和do-while循环
#include <iostream>using namespace std;
int main() { // while循环 int n = 5; while (n > 0) { cout << n << " "; n--; } cout << endl;
// do-while循环(至少执行一次) int m = 0; do { cout << "执行一次" << endl; m++; } while (m < 0); // 即使条件为假,也会执行一次
return 0;}break和continue
#include <iostream>using namespace std;
int main() { // break:跳出循环 for (int i = 1; i <= 10; i++) { if (i == 5) break; cout << i << " "; } cout << endl; // 输出:1 2 3 4
// continue:跳过本次循环 for (int i = 1; i <= 10; i++) { if (i % 2 == 0) continue; // 跳过偶数 cout << i << " "; } cout << endl; // 输出:1 3 5 7 9
return 0;}3. 数组和字符串
3.1 一维数组
#include <iostream>using namespace std;
int main() { // 数组声明和初始化 int arr1[5]; // 未初始化,值不确定 int arr2[5] = {1, 2, 3, 4, 5}; // 完全初始化 int arr3[5] = {1, 2}; // 部分初始化,其余为0 int arr4[] = {1, 2, 3}; // 自动推断大小
// 访问数组元素 cout << "arr2[0] = " << arr2[0] << endl; arr2[0] = 10;
// 遍历数组 for (int i = 0; i < 5; i++) { cout << arr2[i] << " "; } cout << endl;
// 数组作为函数参数 int a[5] = {5, 2, 8, 1, 9}; int n = 5;
// 查找最大值 int maxVal = a[0]; for (int i = 1; i < n; i++) { if (a[i] > maxVal) { maxVal = a[i]; } } cout << "最大值: " << maxVal << endl;
return 0;}3.2 二维数组
#include <iostream>using namespace std;
int main() { // 二维数组声明 int matrix[3][4] = { {1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12} };
// 遍历二维数组 for (int i = 0; i < 3; i++) { for (int j = 0; j < 4; j++) { cout << matrix[i][j] << "\t"; } cout << endl; }
// 应用:矩阵转置 int original[2][3] = {{1,2,3}, {4,5,6}}; int transposed[3][2];
for (int i = 0; i < 2; i++) { for (int j = 0; j < 3; j++) { transposed[j][i] = original[i][j]; } }
cout << "转置后的矩阵:" << endl; for (int i = 0; i < 3; i++) { for (int j = 0; j < 2; j++) { cout << transposed[i][j] << " "; } cout << endl; }
return 0;}3.3 字符串
#include <iostream>#include <string>#include <cstring>using namespace std;
int main() { // C风格字符串 char str1[20] = "Hello"; cout << str1 << endl; cout << "长度: " << strlen(str1) << endl;
// C++风格字符串(推荐) string str2 = "World"; string str3 = "Hello, " + str2 + "!"; // 字符串拼接 cout << str3 << endl;
// 字符串操作 cout << "长度: " << str3.length() << endl; cout << "第一个字符: " << str3[0] << endl;
// 子串 string sub = str3.substr(0, 5); // 从位置0开始,长度为5 cout << "子串: " << sub << endl;
// 查找 size_t pos = str3.find("World"); if (pos != string::npos) { cout << "找到World,位置: " << pos << endl; }
// 字符串比较 string s1 = "abc", s2 = "abd"; if (s1 < s2) { cout << s1 << " 字典序小于 " << s2 << endl; }
// 字符串与数字转换 int num = 123; string numStr = to_string(num); // 数字转字符串 int num2 = stoi("456"); // 字符串转数字 cout << "numStr: " << numStr << ", num2: " << num2 << endl;
return 0;}4. 函数和递归
4.1 函数基础
#include <iostream>using namespace std;
// 函数声明int add(int a, int b);void printArray(int arr[], int n);int factorial(int n);
int main() { int x = 5, y = 3; cout << "和: " << add(x, y) << endl;
int arr[] = {1, 2, 3, 4, 5}; printArray(arr, 5);
cout << "5的阶乘: " << factorial(5) << endl;
return 0;}
// 函数定义int add(int a, int b) { return a + b;}
void printArray(int arr[], int n) { for (int i = 0; i < n; i++) { cout << arr[i] << " "; } cout << endl;}
// 递归函数int factorial(int n) { if (n <= 1) return 1; // 递归终止条件 return n * factorial(n - 1);}4.2 递归详解
递归是OI中最重要的思想之一,很多算法都基于递归实现。
#include <iostream>using namespace std;
// 示例1:斐波那契数列int fibonacci(int n) { if (n <= 1) return n; return fibonacci(n - 1) + fibonacci(n - 2);}
// 示例2:汉诺塔问题void hanoi(int n, char from, char to, char aux) { if (n == 1) { cout << "移动盘子 1 从 " << from << " 到 " << to << endl; return; } hanoi(n - 1, from, aux, to); cout << "移动盘子 " << n << " 从 " << from << " 到 " << to << endl; hanoi(n - 1, aux, to, from);}
// 示例3:二分查找(递归版)int binarySearch(int arr[], int left, int right, int target) { if (left > right) return -1;
int mid = left + (right - left) / 2; if (arr[mid] == target) return mid; else if (arr[mid] > target) return binarySearch(arr, left, mid - 1, target); else return binarySearch(arr, mid + 1, right, target);}
// 示例4:快速幂(递归版)long long quickPow(long long base, long long exp) { if (exp == 0) return 1; long long half = quickPow(base, exp / 2); if (exp % 2 == 0) return half * half; else return half * half * base;}
int main() { // 斐波那契 cout << "fibonacci(10) = " << fibonacci(10) << endl;
// 汉诺塔 cout << "\n汉诺塔问题(3个盘子):" << endl; hanoi(3, 'A', 'C', 'B');
// 二分查找 int arr[] = {1, 3, 5, 7, 9, 11, 13}; int index = binarySearch(arr, 0, 6, 7); cout << "\n查找7的位置: " << index << endl;
// 快速幂 cout << "2^10 = " << quickPow(2, 10) << endl;
return 0;}5. 指针和引用
5.1 指针
#include <iostream>using namespace std;
int main() { int x = 10; int *ptr = &x; // ptr存储x的地址
cout << "x的值: " << x << endl; cout << "x的地址: " << &x << endl; cout << "ptr的值(x的地址): " << ptr << endl; cout << "ptr指向的值: " << *ptr << endl;
// 通过指针修改值 *ptr = 20; cout << "修改后x的值: " << x << endl;
// 数组与指针 int arr[] = {1, 2, 3, 4, 5}; int *p = arr; // 数组名就是指向第一个元素的指针
for (int i = 0; i < 5; i++) { cout << *(p + i) << " "; // 等价于 arr[i] } cout << endl;
return 0;}5.2 引用
引用是变量的别名,在OI中常用于函数参数,避免复制大量数据。
#include <iostream>#include <vector>using namespace std;
// 传值:会复制整个数组,效率低void printByValue(vector<int> v) { for (int x : v) cout << x << " "; cout << endl;}
// 传引用:不复制,直接操作原数据,效率高void printByReference(const vector<int>& v) { for (int x : v) cout << x << " "; cout << endl;}
// 引用可以修改原变量void swap(int& a, int& b) { int temp = a; a = b; b = temp;}
int main() { // 引用的基本使用 int x = 10; int& ref = x; // ref是x的别名 ref = 20; cout << "x = " << x << endl; // 输出20
// 函数中使用引用 int a = 5, b = 3; cout << "交换前: a=" << a << ", b=" << b << endl; swap(a, b); cout << "交换后: a=" << a << ", b=" << b << endl;
// 对于大型数据结构,使用引用提高效率 vector<int> v = {1, 2, 3, 4, 5}; printByReference(v); // 推荐
return 0;}6. STL容器
STL(Standard Template Library)是C++的核心优势,提供了各种高效的数据结构和算法。
6.1 vector(动态数组)
#include <iostream>#include <vector>#include <algorithm>using namespace std;
int main() { // 创建和初始化 vector<int> v1; // 空vector vector<int> v2(5); // 5个元素,默认值为0 vector<int> v3(5, 10); // 5个元素,值为10 vector<int> v4 = {1, 2, 3, 4, 5};
// 添加元素 v1.push_back(1); v1.push_back(2); v1.push_back(3);
// 访问元素 cout << "v1[0] = " << v1[0] << endl; cout << "v1.front() = " << v1.front() << endl; // 第一个元素 cout << "v1.back() = " << v1.back() << endl; // 最后一个元素
// 大小和容量 cout << "size: " << v1.size() << endl; cout << "capacity: " << v1.capacity() << endl;
// 遍历 for (int i = 0; i < v1.size(); i++) { cout << v1[i] << " "; } cout << endl;
// 范围for循环(推荐) for (int x : v1) { cout << x << " "; } cout << endl;
// 删除元素 v1.pop_back(); // 删除最后一个元素 v1.erase(v1.begin()); // 删除第一个元素 v1.clear(); // 清空
// 排序 vector<int> v5 = {5, 2, 8, 1, 9}; sort(v5.begin(), v5.end()); // 升序排序 for (int x : v5) cout << x << " "; cout << endl;
// 二分查找(前提:已排序) bool found = binary_search(v5.begin(), v5.end(), 8); cout << "找到8: " << found << endl;
return 0;}6.2 set和multiset(集合)
#include <iostream>#include <set>using namespace std;
int main() { // set:自动排序,不允许重复 set<int> s; s.insert(3); s.insert(1); s.insert(4); s.insert(1); // 重复,不会插入 s.insert(5);
cout << "set元素: "; for (int x : s) { cout << x << " "; // 输出: 1 3 4 5 } cout << endl;
// 查找 if (s.find(3) != s.end()) { cout << "找到3" << endl; }
// 删除 s.erase(3);
// count:元素个数(set中只能是0或1) cout << "count(1): " << s.count(1) << endl;
// 大小 cout << "size: " << s.size() << endl;
// multiset:允许重复 multiset<int> ms; ms.insert(1); ms.insert(1); ms.insert(2); ms.insert(2); ms.insert(2);
cout << "multiset元素: "; for (int x : ms) { cout << x << " "; // 输出: 1 1 2 2 2 } cout << endl;
cout << "count(2): " << ms.count(2) << endl; // 输出: 3
return 0;}6.3 map(映射)
#include <iostream>#include <map>#include <string>using namespace std;
int main() { // map:键值对,按键排序 map<string, int> score;
// 插入 score["Alice"] = 95; score["Bob"] = 87; score["Charlie"] = 92; score.insert({"David", 88});
// 访问 cout << "Alice的分数: " << score["Alice"] << endl;
// 遍历 for (auto& p : score) { cout << p.first << ": " << p.second << endl; }
// 查找 if (score.find("Bob") != score.end()) { cout << "找到Bob" << endl; }
// 统计元素个数 cout << "count(Alice): " << score.count("Alice") << endl;
// 应用:统计字符出现次数 string text = "hello world"; map<char, int> freq; for (char ch : text) { freq[ch]++; }
cout << "字符频率:" << endl; for (auto& p : freq) { if (p.first != ' ') { cout << p.first << ": " << p.second << endl; } }
return 0;}6.4 queue(队列)
#include <iostream>#include <queue>using namespace std;
int main() { queue<int> q;
// 入队 q.push(1); q.push(2); q.push(3);
// 访问队首和队尾 cout << "队首: " << q.front() << endl; cout << "队尾: " << q.back() << endl;
// 出队 while (!q.empty()) { cout << q.front() << " "; q.pop(); } cout << endl;
// 应用:BFS(广度优先搜索) // 这是队列最常见的应用场景
return 0;}6.5 stack(栈)
#include <iostream>#include <stack>using namespace std;
int main() { stack<int> st;
// 入栈 st.push(1); st.push(2); st.push(3);
// 访问栈顶 cout << "栈顶: " << st.top() << endl;
// 出栈 while (!st.empty()) { cout << st.top() << " "; st.pop(); } cout << endl; // 输出: 3 2 1(后进先出)
// 应用:括号匹配 string s = "{[()]}"; stack<char> brackets; bool valid = true;
for (char ch : s) { if (ch == '(' || ch == '[' || ch == '{') { brackets.push(ch); } else { if (brackets.empty()) { valid = false; break; } char top = brackets.top(); if ((ch == ')' && top == '(') || (ch == ']' && top == '[') || (ch == '}' && top == '{')) { brackets.pop(); } else { valid = false; break; } } }
if (valid && brackets.empty()) { cout << "括号匹配" << endl; } else { cout << "括号不匹配" << endl; }
return 0;}6.6 priority_queue(优先队列)
#include <iostream>#include <queue>#include <vector>using namespace std;
int main() { // 默认是大根堆(最大值在堆顶) priority_queue<int> maxHeap; maxHeap.push(3); maxHeap.push(1); maxHeap.push(4); maxHeap.push(1); maxHeap.push(5);
cout << "大根堆: "; while (!maxHeap.empty()) { cout << maxHeap.top() << " "; maxHeap.pop(); } cout << endl; // 输出: 5 4 3 1 1
// 小根堆(最小值在堆顶) priority_queue<int, vector<int>, greater<int>> minHeap; minHeap.push(3); minHeap.push(1); minHeap.push(4); minHeap.push(1); minHeap.push(5);
cout << "小根堆: "; while (!minHeap.empty()) { cout << minHeap.top() << " "; minHeap.pop(); } cout << endl; // 输出: 1 1 3 4 5
// 应用:找第K大的数 vector<int> nums = {3, 2, 1, 5, 6, 4}; int k = 2; priority_queue<int, vector<int>, greater<int>> pq;
for (int num : nums) { pq.push(num); if (pq.size() > k) { pq.pop(); } }
cout << "第" << k << "大的数: " << pq.top() << endl;
return 0;}7. 输入输出优化
在OI竞赛中,数据量大时,输入输出的效率至关重要。
7.1 快速输入输出
#include <iostream>#include <cstdio>using namespace std;
int main() { // 方法1:关闭同步流(最常用) ios::sync_with_stdio(false); // 关闭C++流与C流的同步 cin.tie(0); // 解除cin与cout的绑定 cout.tie(0);
// 此后使用cin/cout会变快,但不能混用scanf/printf int n; cin >> n; cout << n << endl;
// 方法2:使用scanf和printf(更快,但需要格式化) int a, b; scanf("%d %d", &a, &b); printf("%d\n", a + b);
// 方法3:读入大量整数 // 适合读取大量数据 int x; while (cin >> x) { // 处理x }
return 0;}7.2 多组测试数据
#include <iostream>using namespace std;
int main() { ios::sync_with_stdio(false); cin.tie(0);
int t; // 测试数据组数 cin >> t;
while (t--) { int n; cin >> n; // 处理每组数据 cout << n * 2 << endl; }
return 0;}7.3 读入不定长数组
#include <iostream>#include <vector>using namespace std;
int main() { ios::sync_with_stdio(false); cin.tie(0);
// 方法1:先读n,再读n个数 int n; cin >> n; vector<int> arr(n); for (int i = 0; i < n; i++) { cin >> arr[i]; }
// 方法2:读到文件末尾 vector<int> data; int x; while (cin >> x) { data.push_back(x); }
return 0;}8. 常用技巧和注意事项
8.1 常用算法函数
#include <iostream>#include <vector>#include <algorithm>#include <cmath>using namespace std;
int main() { vector<int> v = {3, 1, 4, 1, 5, 9, 2, 6};
// 排序 sort(v.begin(), v.end()); // 升序 sort(v.begin(), v.end(), greater<int>()); // 降序
// 查找最大最小值 int maxVal = *max_element(v.begin(), v.end()); int minVal = *min_element(v.begin(), v.end()); cout << "最大值: " << maxVal << ", 最小值: " << minVal << endl;
// 求和 int sum = 0; for (int x : v) sum += x; cout << "和: " << sum << endl;
// 反转 reverse(v.begin(), v.end());
// 去重(前提:已排序) v.erase(unique(v.begin(), v.end()), v.end());
// 下一个排列 vector<int> perm = {1, 2, 3}; do { for (int x : perm) cout << x << " "; cout << endl; } while (next_permutation(perm.begin(), perm.end()));
// 数学函数 cout << "sqrt(16) = " << sqrt(16) << endl; cout << "pow(2, 10) = " << pow(2, 10) << endl; cout << "abs(-5) = " << abs(-5) << endl; cout << "max(3, 7) = " << max(3, 7) << endl; cout << "min(3, 7) = " << min(3, 7) << endl;
return 0;}8.2 常见陷阱
#include <iostream>#include <vector>#include <climits>using namespace std;
int main() { // 陷阱1:整数溢出 int a = 1e9; int b = 1e9; // long long c = a * b; // 错误!a*b先计算,已经溢出 long long c = (long long)a * b; // 正确 cout << "c = " << c << endl;
// 陷阱2:数组越界 vector<int> v(5); // v[5] = 10; // 越界!数组下标是0-4
// 陷阱3:除零错误 int x = 10, y = 0; if (y != 0) { cout << x / y << endl; } else { cout << "不能除以0" << endl; }
// 陷阱4:浮点数比较 double d1 = 0.1 + 0.2; double d2 = 0.3; const double eps = 1e-9; if (abs(d1 - d2) < eps) { // 使用误差范围比较 cout << "相等" << endl; }
// 陷阱5:死循环 // for (int i = 0; i < 10; i--) { } // 死循环
// 陷阱6:忘记初始化 int arr[5]; // 未初始化,值不确定 // 应该:int arr[5] = {0};
// 陷阱7:负数取模 int mod = 1e9 + 7; int neg = -5; int result = ((neg % mod) + mod) % mod; // 保证非负 cout << "result = " << result << endl;
return 0;}8.3 调试技巧
#include <iostream>#include <vector>#include <cassert>using namespace std;
// 调试宏#define debug(x) cout << #x << " = " << x << endl#define debugArr(arr, n) \ do { \ cout << #arr << " = ["; \ for (int i = 0; i < n; i++) { \ cout << arr[i]; \ if (i < n - 1) cout << ", "; \ } \ cout << "]" << endl; \ } while(0)
int main() { int x = 10; debug(x); // 输出: x = 10
int arr[] = {1, 2, 3, 4, 5}; debugArr(arr, 5); // 输出: arr = [1, 2, 3, 4, 5]
// 断言(检查条件是否成立) int n = 5; assert(n > 0); // 如果n<=0,程序会终止并报错
// 条件编译(本地调试时启用) #ifdef LOCAL freopen("input.txt", "r", stdin); freopen("output.txt", "w", stdout); #endif
return 0;}8.4 模板代码
OI竞赛常用的模板框架:
#include <iostream>#include <vector>#include <queue>#include <stack>#include <set>#include <map>#include <algorithm>#include <cmath>#include <cstring>using namespace std;
typedef long long ll;typedef pair<int, int> pii;
const int MAXN = 1e5 + 5;const int MOD = 1e9 + 7;const int INF = 0x3f3f3f3f;
int n, m;int arr[MAXN];vector<int> adj[MAXN];
void solve() { // 解题代码}
int main() { ios::sync_with_stdio(false); cin.tie(0);
int t = 1; // cin >> t; // 多组测试数据时取消注释
while (t--) { solve(); }
return 0;}总结
本文详细介绍了OI竞赛中C++的核心语法知识,从基础数据类型到STL容器,从控制结构到递归思想,涵盖了竞赛编程的方方面面。掌握这些知识点后,建议多做练习题,将理论转化为实践能力。
学习路线建议:
- 基础阶段:熟练掌握基本语法、数组、函数
- 进阶阶段:深入学习STL容器、算法库
- 高级阶段:结合具体算法(如图论、动态规划)进行综合应用
刷题网站推荐:
- 洛谷(luogu.com.cn)
- Codeforces
- AtCoder
- LeetCode
记住,编程能力的提升需要大量练习。多写代码,多思考,多总结,你一定能在OI竞赛中取得好成绩!加油!
OI竞赛C++语法详解
https://myblog-590.pages.dev/posts/cpp-syntax/