L01 暑期诊断 + 算法底座

先建第一套读题工作流:数据范围 → 估复杂度 → 识别模型 → 选模板 → 造边界数据。不是背更多算法,是"有没有活路"的判断力。

一、先回答问题

本节定位

这节课不扩新知识,而是建立暑期训练的第一套读题工作流。拿到题直接敲代码的习惯会让你在 CSP-J T3/T4 上翻车。

两名学生目标

蛋*:搜索 / 背包 / 模拟 / 读题边界,把基础题写稳。
丁*:图论建模 / DP 分类 / 复杂度 / 对拍,提高题写快写准。


问题课堂答案
拿到题先做什么?先看数据范围和问题目标,不急着写代码。
数据范围有什么用?反推可接受复杂度,排除不可能的算法。
为什么不能随便贪心?贪心必须证明局部选择不会破坏全局最优。
为什么要做诊断?暑期计划必须按真实短板分层,不按"听过什么"判断。

二、本节知识点地图

知识点要达到的程度课堂检查
数据范围反推复杂度看到 n 能排除明显不可能的算法口答 6 个数据范围
常见复杂度量级知道 O(n!)、O(2^n)、O(n^3)、O(n log n) 的差别复杂度排序练习
模型识别能从题面信号判断模拟、搜索、二分、图论、DP模型卡片匹配
贪心警惕知道"看起来最优"不等于正确Forming Quiz Teams 反例讨论
错因分类不再写"粗心",能归因到 6 类错误错因表现场填写
诊断复盘能给自己定第一周补救目标课后 3 分钟自评

三、概念讲解

概念 1:复杂度是赛场生存规则

复杂度不是让你背定义,而是让你在写代码前先判断"这个方法有没有活路"。

如果一个算法需要做 1012 次操作,哪怕代码写得漂亮,也不可能在 1 秒内跑完。

概念 2:模型是题目的"骨架"

题目表面是故事,算法题真正考的是背后的模型。每道题都写一句:

这题的模型是 ______,因为题面出现了 ______。


概念 3:贪心必须证明

错误说法:"每次选最近的,看起来总和就会最小。"
正确追问:"你这一步选了以后,后面的选择空间有没有被破坏?有没有一个反例能让它失败?"

概念 4:错因要能指导训练

"粗心"无法指导训练。
错因写"边界" → 补 3 组边界数据;写"模板" → 默写模板;写"复杂度" → 重选算法。


循环层数工作量直觉
一层循环走一遍所有数据
两层循环每个数都和每个数配对
三层循环每对关系还要再枚举一个中间点
递归枚举分叉树,增长很快

四、思维过程:复杂度天梯表

一秒大约承受 108 量级的简单操作(粗略估算,帮助排除明显不可能的算法)。

数据范围常见可尝试复杂度可以想到不能乱用
n <= 10O(n!)、小规模 DFS全排列、搜索树更复杂算法不一定值得
n <= 20O(2^n)、O(n·2^n)子集枚举、状态压缩20! 不可行
n <= 500O(n^3)Floyd、区间 DP四重循环危险
n <= 5000O(n^2)LIS/LCS、二维 DP三重循环通常危险
n <= 1e5O(n log n)排序、堆、二分、Dijkstra双重循环通常 TLE
n <= 1e6O(n) 或接近线性前缀和、双指针、线性筛O(n log n) 要看时限
n >= 1e9O(log n)、O(1)、O(√n)数学、快速幂、二分逐个枚举不可能

课堂手推 1:n=20 为何不全排列?

10! = 3,628,800,而 20! 已经远超可接受范围。
但 220 = 1,048,576 ≈ 1e6,完全可以做。
所以 n <= 20 常暗示"选或不选""状压 DP",而非全排列。

课堂手推 2:n=500 为何可想 Floyd?

Floyd 是三重循环:5003 = 125,000,000 ≈ 1.25e8,C++ 中可候选(还要看时限和常数)。
但若 n=5000:50003 = 1.25e11,明显不合适。

五、模型识别:不要猜算法

读题不是把文字读完,而是找"信号"。

题面信号先想到第一问
按规则一步步变化模拟状态和操作是什么?
求所有方案 / 连通块 / 最短步数搜索状态是什么?如何判重?
最大化最小值 / 最小化最大值二分答案check(x) 是否单调?
点、边、路径、依赖图论点和边分别是什么?
最优值 / 方案数 / 子问题重复DP状态、转移、边界、顺序是什么?

故事可能模型
机器内存放单词队列模拟
湖中水域连成片Flood Fill
采药有时间和价值0-1 背包
石头之间跳跃距离二分答案
城市之间道路图论最短路

六、贪心反例:Forming Quiz Teams

题意

有 2N 个人,每人有坐标,要把他们两两配对,使所有配对距离之和最小。

学生直觉:每次找当前距离最近的两个人配成一组。

反例

4 个点 A、B、C、D:A-B 很近,但配了 A-B 后,C-D 特别远。
也许 A-C + B-D 的总和反而更小。

结论:局部最近,不一定全局最短。贪心必须能证明。


模型演进路径

错误直觉(贪心)→ 正确但太慢(枚举所有配对)→ 状态压缩 DP(二进制记录谁已配对)。这是很多竞赛题的真实成长路径。本节只要求建立警惕,不要求掌握状压 DP。

七、互动演示:选数据范围 → 判断算法

当前 n20
量级O(2^n)
结论可候选
手推验证

          

每次切换挡位,右侧显示手推计算过程。

八、六类错因:不要再说"粗心"

错因典型表现订正动作
读题条件漏看、输出格式错复述题意,标关键词
边界n=1、全相同、最大值错自造 3 组边界
模板背包、DFS、Dijkstra 写不出默写模板
复杂度暴力 TLE、数组爆内存写出量级
调试样例过但 WA/RE打印、GDB、对拍
实现下标、初始化、循环方向错写个人 checklist

课堂强调

"粗心"不是错因。粗心背后一定是读题、边界、模板、复杂度、调试或实现中的一个具体问题。以后错题必须归到 6 类之一。

九、例题精讲

P1596 湖计数(Flood Fill)

题面信号

网格、水域(W)、相邻(8 方向)、连成一片算同一片湖,问共有几片湖。

模型判断

Flood Fill(连通块计数)。每次遇到未访问的 W,从它出发把整个连通块染色,计数 +1。


检查点容易错在哪
方向数组是 8 个方向(含对角线),不是 4 个
越界判断行列下标都要判,放在访问前
visited 时机进入 DFS 即标记,防止同一块被重复计数
计数逻辑一个连通块只在第一次发现时计数 +1,内部递归不再计数

P1048 采药(0-1 背包)

题面信号

每株药有时间(重量)和价值,每株只能采一次,总时间有限(容量),求最大价值。

模型判断

0-1 背包。每件物品选或不选,dp[j] = 时间恰好为 j 时可获得的最大价值。


检查点容易错在哪
dp[j] 含义剩余时间为 j 时能取到的最大价值
容量倒序必须从大到小枚举 j,否则同一株药会被选多次(变成完全背包)
初值dp[0..T] = 0,表示什么也不选时价值为 0
时间超限若某株药时间 > T,直接跳过(j 从 T 到 w[i] 的范围不会执行)

C++14 核心代码:P1048 采药(0-1 背包)

#include <bits/stdc++.h>
using namespace std;

int T, M;              // T: 总时间, M: 草药数量
int t[1005], v[1005];  // t[i]: 采药时间, v[i]: 价值
int dp[1005];          // dp[j]: 用时恰好为 j 时的最大价值

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    cin >> T >> M;
    for (int i = 1; i <= M; i++)
        cin >> t[i] >> v[i];

    // 0-1 背包:物品循环在外,容量倒序在内
    for (int i = 1; i <= M; i++) {
        for (int j = T; j >= t[i]; j--) {  // 倒序!防止一株药选多次
            dp[j] = max(dp[j], dp[j - t[i]] + v[i]);
        }
    }
    cout << dp[T] << "\n";
    return 0;
}

读代码重点:倒序枚举容量是 0-1 背包的核心,正序枚举是完全背包。两种只差一个方向,含义完全不同。

十、当堂习题

互动选择题

问题 1

n = 20,每个物品可以选或不选。合理的复杂度?

问题 2

看到"每个物品只能选一次,总容量有限,求最大价值",先想到?

问题 3

0-1 背包里为什么容量要倒序枚举?

问题 4

错题归因,下面哪个写法是正确错因?

分层作业表

学生题目要求
蛋*P1596 湖计数独立 AC,说清连通块只计一次,自造 3 个边界
蛋*P1048 采药独立 AC,解释为什么容量倒序
丁*P4779 单源最短路默写 Dijkstra 结构,解释堆优化
丁*P1880 石子合并写出状态、转移、枚举顺序,处理环形
全员错因表今天每个错误必须归到 6 类之一

十一、诊断安排

内容时间目的
选择判断 A 组20 分钟看基础概念和复杂度反应
编程诊断45 分钟看模板熟练度和代码稳定性
自评5 分钟让学生说出自己的卡点

蛋* 诊断题

P1596 湖计数:Flood Fill、边界、visited。
P1048 采药:0-1 背包、逆序循环。

丁* 诊断题

P4779 单源最短路:Dijkstra、堆、建图。
P1880 石子合并:区间 DP、环形处理。

十二、课堂验收

下课前必须答出

1. n <= 20 为什么常想到 2^n?
2. n <= 500 为什么可能用 Floyd?
3. 为什么贪心不能只凭直觉?
4. P1596 是什么模型?检查点有哪些?
5. P1048 是什么模型?为什么容量倒序?
6. 错因为什么不能写"粗心"?