知识公社 | 课程 · 算法竞赛从零到精通
ALGORITHM CONTEST · ZERO TO HERO

算法竞赛 · 从零到精通 · 13 讲

面向零基础的完整讲解:每讲都有知识点、可运行的代码模板、经典例题和练习指引。

建议节奏:每天一讲 + 当天完成对应练习;13 天走完基础盘,然后按最后一讲的刷题路线持续推进。

本课配套的免费练习平台(点开即可做题,全部免费):

不会注册?第 1 讲里写了每一步。做题时遇到不会的,先看最后一讲的"卡住了怎么办"。

第 1 讲 · 开始之前:环境、平台与心态

目标:能在电脑装好环境、在平台上注册并提交第一道题。

1. 选一门语言

2. 装环境(10 分钟)

#include <bits/stdc++.h>
using namespace std;
int main(){
    int a, b;
    cin >> a >> b;      // 读入两个数
    cout << a + b << endl; // 输出它们的和
    return 0;
}

3. 注册一个平台并提交第一题

小测 · 提交代码后,下面哪种状态表示"通过了"?
本讲小测:完成 A+B Problem 的提交(本站索引搜 "A+B" 或直接用平台的第一题)。

第 2 讲 · 复杂度:让你的程序"跑得动"

目标:看到题目数据范围,就能判断自己的算法会不会超时。

竞技编程的第一思维不是"对不对",而是"快不快"。评测机大约每秒能执行 1 亿次左右的基本操作。

记一张速查表(1 秒时限内)

例子

// 求 1+2+...+n
// 慢:循环 O(n)
long long s = 0;
for (int i = 1; i <= n; i++) s += i;
// 快:公式 O(1)
long long s2 = 1LL * n * (n + 1) / 2;
小测 · 竞赛评测机 1 秒钟大约能执行多少次基本操作?
小测:n = 10⁹ 时上面两种写法哪个能过?(答案:公式;循环要跑 10 秒以上,必超时)

第 3 讲 · 数组、字符串与"模拟"

目标:把题面规则原样翻译成代码(模拟题是新手最稳定的得分点)。

要点

例题(洛谷风格):统计一句话里的大写字母数量

string s; getline(cin, s);
int cnt = 0;
for (char c : s)
    if (c >= 'A' && c <= 'Z') cnt++;
cout << cnt << endl;
小测 · 新手做"模拟题"最容易栽在哪?
练习:把上面的题改成统计"数字字符"的个数,再改成统计"每个字母出现次数"(提示:数组计数)。

第 4 讲 · 排序与二分

目标:会用 sort;会写"猜答案"的二分。

排序:一行解决

int a[1005], n;
sort(a, a + n);              // 升序
sort(a, a + n, greater<int>()); // 降序
// 结构体排序
struct P { int x, y; };
bool cmp(const P& a, const P& b){ return a.x < b.x; }
sort(p, p + m, cmp);

二分:在"单调世界"里找答案

两种用法:一、 在有序数组里找某个值;二、 答案具有单调性时二分答案("最大的最小值""能不能做到"类问题)。

// 在升序数组 a 里找 x 是否存在
int l = 0, r = n - 1;
while (l <= r) {
    int mid = (l + r) / 2;
    if (a[mid] == x) { /* 找到 */ break; }
    if (a[mid] < x) l = mid + 1; else r = mid - 1;
}
小测 · 哪类问题适合用"二分答案"?
练习:搜本站题库 "binary search" 标签的 1200 分题做 5 道。

第 5 讲 · 前缀和与差分

目标:把"区间求和 / 区间修改"从 O(n) 变成 O(1)。

前缀和(快速求区间和)

long long pre[100005];
for (int i = 1; i <= n; i++) pre[i] = pre[i-1] + a[i];
// 区间 [l, r] 的和:
long long ans = pre[r] - pre[l-1];

差分(快速区间加)

// 让 [l, r] 全部 +v:
diff[l] += v; diff[r+1] -= v;
// 结束后求前缀和还原:
for (int i = 1; i <= n; i++) a[i] = a[i-1] + diff[i];
小测 · 把区间 [l, r] 整体 +v,用差分的正确做法是?
练习:经典题"小明的项链"+ 任意两道"区间加、单点查"的题。

第 6 讲 · 栈、队列与链表

目标:知道三种结构的用途,并能用栈解决括号匹配、用队列做"逐层处理"。
// 括号匹配
stack<char> st; bool ok = true;
for (char c : s) {
    if (c == '(') st.push(c);
    else if (c == ')') {
        if (st.empty()) { ok = false; break; }
        st.pop();
    }
}
if (!st.empty()) ok = false;
cout << (ok ? "YES" : "NO") << endl;
小测 · 判断括号是否匹配,该用什么结构?
练习:把括号匹配扩展为同时支持 ()[]{} 三种括号。

第 7 讲 · 递归与深度优先搜索(DFS)

目标:会写递归,能用 DFS 遍历图和"暴力枚举所有可能"。

递归三要素

// 求 n 的阶乘(最简单的递归)
long long fac(int n) {
    if (n <= 1) return 1;      // 终止
    return n * fac(n - 1);      // 缩小规模
}
// DFS 框架(网格)
int n, m; bool vis[1005][1005];
int dx[4] = {1, -1, 0, 0}, dy[4] = {0, 0, 1, -1};
void dfs(int x, int y) {
    vis[x][y] = true;
    for (int k = 0; k < 4; k++) {
        int nx = x + dx[k], ny = y + dy[k];
        if (nx >= 0 && nx < n && ny >= 0 && ny < m && !vis[nx][ny])
            dfs(nx, ny);
    }
}
小测 · DFS 里的"回溯"是指什么?
练习:求"迷宫从左上到右下能不能走通";再做一道"全排列"(回溯入门经典)。

第 8–13 讲(BFS、贪心、动态规划入门、图论入门、并查集与最小生成树、刷题路线图)在下方继续……

也可直接跳到:继续学习第 8–13 讲 →(如果页面太长,分页版在这里)