diff --git a/README.md b/README.md index da40abe..217e1e2 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,7 @@ - 五阶段课程路径与阶段切换; - 40 节课件的学习目标、讲授内容、检验清单和练习建议; +- 每节课的先修关系:这一课要先会哪几课、具体要先会哪一件事、上一课是哪一课,都写在页首标题正下方,点得进去; - 本地保存的逐项学习进度,不收集账号或个人信息;浏览器拒绝保存时会明确说明,并可在恢复后重试补写; - 适合外部录课制作时使用的“视频组织建议”和移动端阅读布局。 @@ -282,3 +283,19 @@ CSP_E2E_LESSONS=s3-07 bun tests/e2e/flow-cdp.ts # 只跑一课 新加的课自动纳入检查。步骤与仍需人工确认的部分见 [`tests/e2e/keyboard-manual-checklist.md`](./tests/e2e/keyboard-manual-checklist.md)。 +## 先修关系可见与可达复验 + +「进页面第一眼就知道这节课要先会什么,而且点得回去」同样有自动化复验:`bun run e2e:prereq` 在真实浏览器里 +对全部 40 课逐课复验,242 项检查(40 课各 6 项:5 项笔记本视口 + 1 项手机竖屏,另含脚本自检 2 项)。 + +每课六项:先修块(`
C++ 跑起来这一课不要求你背很多单词。你只要完成四个小任务:看懂程序入口、走一遍编译流程、修复一个小错误,再做一套随机小测。
diff --git a/lessons/s1-02/index.html b/lessons/s1-02/index.html index d8924df..56027ae 100644 --- a/lessons/s1-02/index.html +++ b/lessons/s1-02/index.html @@ -951,6 +951,58 @@ background: rgba(24, 55, 47, 0.1); } + .prereq-note { + margin-top: 18px; + padding: 14px 16px; + border: 2px dashed rgba(24, 55, 47, 0.28); + border-radius: 6px; + background: rgba(255, 255, 255, 0.62); + } + + .prereq-note > strong { + display: block; + color: var(--forest); + font-family: "Songti SC", "STSong", serif; + font-size: 1rem; + } + + .prereq-list { + margin: 8px 0 0; + padding-left: 18px; + font-size: 0.78rem; + line-height: 1.65; + } + + .prereq-list li + li { + margin-top: 5px; + } + + .prereq-list span { + display: block; + color: rgba(24, 55, 47, 0.78); + } + + .prereq-link { + color: var(--forest); + font-weight: 800; + text-decoration: underline; + text-decoration-thickness: 2px; + text-underline-offset: 2px; + } + + .prereq-link:hover { + color: var(--orange); + } + + .prereq-path, + .prereq-empty { + margin: 9px 0 0; + color: rgba(24, 55, 47, 0.82); + font-size: 0.76rem; + line-height: 1.6; + } + + .type-grid { display: grid; gap: 12px; @@ -1308,6 +1360,13 @@盒子这一课只练一件事:用变量把题目里的量装进合适的类型。完成四个小任务:选类型、更新变量、分清字符和大整数,再做一套随机小测。
diff --git a/lessons/s1-03/index.html b/lessons/s1-03/index.html index 18c7dd8..5e7275c 100644 --- a/lessons/s1-03/index.html +++ b/lessons/s1-03/index.html @@ -951,6 +951,58 @@ background: rgba(24, 55, 47, 0.1); } + .prereq-note { + margin-top: 18px; + padding: 14px 16px; + border: 2px dashed rgba(24, 55, 47, 0.28); + border-radius: 6px; + background: rgba(255, 255, 255, 0.62); + } + + .prereq-note > strong { + display: block; + color: var(--forest); + font-family: "Songti SC", "STSong", serif; + font-size: 1rem; + } + + .prereq-list { + margin: 8px 0 0; + padding-left: 18px; + font-size: 0.78rem; + line-height: 1.65; + } + + .prereq-list li + li { + margin-top: 5px; + } + + .prereq-list span { + display: block; + color: rgba(24, 55, 47, 0.78); + } + + .prereq-link { + color: var(--forest); + font-weight: 800; + text-decoration: underline; + text-decoration-thickness: 2px; + text-underline-offset: 2px; + } + + .prereq-link:hover { + color: var(--orange); + } + + .prereq-path, + .prereq-empty { + margin: 9px 0 0; + color: rgba(24, 55, 47, 0.82); + font-size: 0.76rem; + line-height: 1.6; + } + + .type-grid { display: grid; gap: 12px; @@ -1349,6 +1401,13 @@算式这一课只练一件事:把文字公式翻译成 C++ 。完成四个小任务:选对公式、看清整除和余数、主动加括号,再做一套随机小测。
diff --git a/lessons/s1-04/index.html b/lessons/s1-04/index.html index e3f5774..e0aa8f5 100644 --- a/lessons/s1-04/index.html +++ b/lessons/s1-04/index.html @@ -951,6 +951,58 @@ background: rgba(24, 55, 47, 0.1); } + .prereq-note { + margin-top: 18px; + padding: 14px 16px; + border: 2px dashed rgba(24, 55, 47, 0.28); + border-radius: 6px; + background: rgba(255, 255, 255, 0.62); + } + + .prereq-note > strong { + display: block; + color: var(--forest); + font-family: "Songti SC", "STSong", serif; + font-size: 1rem; + } + + .prereq-list { + margin: 8px 0 0; + padding-left: 18px; + font-size: 0.78rem; + line-height: 1.65; + } + + .prereq-list li + li { + margin-top: 5px; + } + + .prereq-list span { + display: block; + color: rgba(24, 55, 47, 0.78); + } + + .prereq-link { + color: var(--forest); + font-weight: 800; + text-decoration: underline; + text-decoration-thickness: 2px; + text-underline-offset: 2px; + } + + .prereq-link:hover { + color: var(--orange); + } + + .prereq-path, + .prereq-empty { + margin: 9px 0 0; + color: rgba(24, 55, 47, 0.82); + font-size: 0.76rem; + line-height: 1.6; + } + + .type-grid { display: grid; gap: 12px; @@ -1349,6 +1401,14 @@样例这一课只练一件事:用 读入、用 按格式输出。完成四个小任务:选对读入、看清空格和换行、拿掉多余提示语,再做一套随机小测。
diff --git a/lessons/s1-05/index.html b/lessons/s1-05/index.html index 54e3e84..d60e81a 100644 --- a/lessons/s1-05/index.html +++ b/lessons/s1-05/index.html @@ -951,6 +951,58 @@ background: rgba(24, 55, 47, 0.1); } + .prereq-note { + margin-top: 18px; + padding: 14px 16px; + border: 2px dashed rgba(24, 55, 47, 0.28); + border-radius: 6px; + background: rgba(255, 255, 255, 0.62); + } + + .prereq-note > strong { + display: block; + color: var(--forest); + font-family: "Songti SC", "STSong", serif; + font-size: 1rem; + } + + .prereq-list { + margin: 8px 0 0; + padding-left: 18px; + font-size: 0.78rem; + line-height: 1.65; + } + + .prereq-list li + li { + margin-top: 5px; + } + + .prereq-list span { + display: block; + color: rgba(24, 55, 47, 0.78); + } + + .prereq-link { + color: var(--forest); + font-weight: 800; + text-decoration: underline; + text-decoration-thickness: 2px; + text-underline-offset: 2px; + } + + .prereq-link:hover { + color: var(--orange); + } + + .prereq-path, + .prereq-empty { + margin: 9px 0 0; + color: rgba(24, 55, 47, 0.82); + font-size: 0.76rem; + line-height: 1.6; + } + + .type-grid { display: grid; gap: 12px; @@ -1370,6 +1422,14 @@分支这一课只练一件事:用 根据条件选择不同分支。完成四个小任务:选对判断、走一遍 60 分边界、分清 和 ,再做一套随机小测。
diff --git a/lessons/s1-06/index.html b/lessons/s1-06/index.html index b125935..8a6f29e 100644 --- a/lessons/s1-06/index.html +++ b/lessons/s1-06/index.html @@ -951,6 +951,58 @@ background: rgba(24, 55, 47, 0.1); } + .prereq-note { + margin-top: 18px; + padding: 14px 16px; + border: 2px dashed rgba(24, 55, 47, 0.28); + border-radius: 6px; + background: rgba(255, 255, 255, 0.62); + } + + .prereq-note > strong { + display: block; + color: var(--forest); + font-family: "Songti SC", "STSong", serif; + font-size: 1rem; + } + + .prereq-list { + margin: 8px 0 0; + padding-left: 18px; + font-size: 0.78rem; + line-height: 1.65; + } + + .prereq-list li + li { + margin-top: 5px; + } + + .prereq-list span { + display: block; + color: rgba(24, 55, 47, 0.78); + } + + .prereq-link { + color: var(--forest); + font-weight: 800; + text-decoration: underline; + text-decoration-thickness: 2px; + text-underline-offset: 2px; + } + + .prereq-link:hover { + color: var(--orange); + } + + .prereq-path, + .prereq-empty { + margin: 9px 0 0; + color: rgba(24, 55, 47, 0.82); + font-size: 0.76rem; + line-height: 1.6; + } + + .type-grid { display: grid; gap: 12px; @@ -1349,6 +1401,14 @@循环这一课只练一件事:用 把重复计算写成循环。完成四个小任务:选对循环、走一遍累加、避免 ,再做一套随机小测。
diff --git a/lessons/s1-07/index.html b/lessons/s1-07/index.html index 69aa8cd..d76dddd 100644 --- a/lessons/s1-07/index.html +++ b/lessons/s1-07/index.html @@ -951,6 +951,58 @@ background: rgba(24, 55, 47, 0.1); } + .prereq-note { + margin-top: 18px; + padding: 14px 16px; + border: 2px dashed rgba(24, 55, 47, 0.28); + border-radius: 6px; + background: rgba(255, 255, 255, 0.62); + } + + .prereq-note > strong { + display: block; + color: var(--forest); + font-family: "Songti SC", "STSong", serif; + font-size: 1rem; + } + + .prereq-list { + margin: 8px 0 0; + padding-left: 18px; + font-size: 0.78rem; + line-height: 1.65; + } + + .prereq-list li + li { + margin-top: 5px; + } + + .prereq-list span { + display: block; + color: rgba(24, 55, 47, 0.78); + } + + .prereq-link { + color: var(--forest); + font-weight: 800; + text-decoration: underline; + text-decoration-thickness: 2px; + text-underline-offset: 2px; + } + + .prereq-link:hover { + color: var(--orange); + } + + .prereq-path, + .prereq-empty { + margin: 9px 0 0; + color: rgba(24, 55, 47, 0.82); + font-size: 0.76rem; + line-height: 1.6; + } + + .type-grid { display: grid; gap: 12px; @@ -1349,6 +1401,13 @@嵌套循环这一课只练一件事:用 看懂二维重复。完成四个小任务:分清内外层、走一遍 3 行 3 列、认识 ,再做一套随机小测。
diff --git a/lessons/s1-08/index.html b/lessons/s1-08/index.html index 4240b14..04d524a 100644 --- a/lessons/s1-08/index.html +++ b/lessons/s1-08/index.html @@ -951,6 +951,58 @@ background: rgba(24, 55, 47, 0.1); } + .prereq-note { + margin-top: 18px; + padding: 14px 16px; + border: 2px dashed rgba(24, 55, 47, 0.28); + border-radius: 6px; + background: rgba(255, 255, 255, 0.62); + } + + .prereq-note > strong { + display: block; + color: var(--forest); + font-family: "Songti SC", "STSong", serif; + font-size: 1rem; + } + + .prereq-list { + margin: 8px 0 0; + padding-left: 18px; + font-size: 0.78rem; + line-height: 1.65; + } + + .prereq-list li + li { + margin-top: 5px; + } + + .prereq-list span { + display: block; + color: rgba(24, 55, 47, 0.78); + } + + .prereq-link { + color: var(--forest); + font-weight: 800; + text-decoration: underline; + text-decoration-thickness: 2px; + text-underline-offset: 2px; + } + + .prereq-link:hover { + color: var(--orange); + } + + .prereq-path, + .prereq-empty { + margin: 9px 0 0; + color: rgba(24, 55, 47, 0.82); + font-size: 0.76rem; + line-height: 1.6; + } + + .type-grid { display: grid; gap: 12px; @@ -1349,6 +1401,14 @@这一课只练一件事:建立“先看报错、再改写法、再用小数据验证”的工作流。完成四个小任务:分清 、 和 ,定位一处语法错,再用小数据和 检查。
diff --git a/lessons/s2-01/index.html b/lessons/s2-01/index.html index b3e53dc..b382564 100644 --- a/lessons/s2-01/index.html +++ b/lessons/s2-01/index.html @@ -991,6 +991,58 @@ background: rgba(24, 55, 47, 0.1); } + .prereq-note { + margin-top: 18px; + padding: 14px 16px; + border: 2px dashed rgba(24, 55, 47, 0.28); + border-radius: 6px; + background: rgba(255, 255, 255, 0.62); + } + + .prereq-note > strong { + display: block; + color: var(--forest); + font-family: "Songti SC", "STSong", serif; + font-size: 1rem; + } + + .prereq-list { + margin: 8px 0 0; + padding-left: 18px; + font-size: 0.78rem; + line-height: 1.65; + } + + .prereq-list li + li { + margin-top: 5px; + } + + .prereq-list span { + display: block; + color: rgba(24, 55, 47, 0.78); + } + + .prereq-link { + color: var(--forest); + font-weight: 800; + text-decoration: underline; + text-decoration-thickness: 2px; + text-underline-offset: 2px; + } + + .prereq-link:hover { + color: var(--orange); + } + + .prereq-path, + .prereq-empty { + margin: 9px 0 0; + color: rgba(24, 55, 47, 0.82); + font-size: 0.76rem; + line-height: 1.6; + } + + .type-grid { display: grid; gap: 12px; @@ -1350,6 +1402,14 @@这一课只练一件事:用 装一组同类数据,并安全使用 。完成四个小任务:选出怎么声明 int a[n],走一遍 a[0] 到 a[n - 1],再遍历求最大值。这里不会在浏览器里运行 C++。
diff --git a/lessons/s2-02/index.html b/lessons/s2-02/index.html index e176ea6..e3dc7d0 100644 --- a/lessons/s2-02/index.html +++ b/lessons/s2-02/index.html @@ -991,6 +991,58 @@ background: rgba(24, 55, 47, 0.1); } + .prereq-note { + margin-top: 18px; + padding: 14px 16px; + border: 2px dashed rgba(24, 55, 47, 0.28); + border-radius: 6px; + background: rgba(255, 255, 255, 0.62); + } + + .prereq-note > strong { + display: block; + color: var(--forest); + font-family: "Songti SC", "STSong", serif; + font-size: 1rem; + } + + .prereq-list { + margin: 8px 0 0; + padding-left: 18px; + font-size: 0.78rem; + line-height: 1.65; + } + + .prereq-list li + li { + margin-top: 5px; + } + + .prereq-list span { + display: block; + color: rgba(24, 55, 47, 0.78); + } + + .prereq-link { + color: var(--forest); + font-weight: 800; + text-decoration: underline; + text-decoration-thickness: 2px; + text-underline-offset: 2px; + } + + .prereq-link:hover { + color: var(--orange); + } + + .prereq-path, + .prereq-empty { + margin: 9px 0 0; + color: rgba(24, 55, 47, 0.82); + font-size: 0.76rem; + line-height: 1.6; + } + + .type-grid { display: grid; gap: 12px; @@ -1350,6 +1402,14 @@这一课只练一件事:分清 和 ,再按 逐个访问字符。完成四个小任务:选出 char c = 'A' 和 string s = "Ada",走一遍 s[0] 到 s[n - 1],再统计指定字符。这里不会在浏览器里运行 C++。
diff --git a/lessons/s2-03/index.html b/lessons/s2-03/index.html index 09fc648..63bf3d9 100644 --- a/lessons/s2-03/index.html +++ b/lessons/s2-03/index.html @@ -991,6 +991,58 @@ background: rgba(24, 55, 47, 0.1); } + .prereq-note { + margin-top: 18px; + padding: 14px 16px; + border: 2px dashed rgba(24, 55, 47, 0.28); + border-radius: 6px; + background: rgba(255, 255, 255, 0.62); + } + + .prereq-note > strong { + display: block; + color: var(--forest); + font-family: "Songti SC", "STSong", serif; + font-size: 1rem; + } + + .prereq-list { + margin: 8px 0 0; + padding-left: 18px; + font-size: 0.78rem; + line-height: 1.65; + } + + .prereq-list li + li { + margin-top: 5px; + } + + .prereq-list span { + display: block; + color: rgba(24, 55, 47, 0.78); + } + + .prereq-link { + color: var(--forest); + font-weight: 800; + text-decoration: underline; + text-decoration-thickness: 2px; + text-underline-offset: 2px; + } + + .prereq-link:hover { + color: var(--orange); + } + + .prereq-path, + .prereq-empty { + margin: 9px 0 0; + color: rgba(24, 55, 47, 0.82); + font-size: 0.76rem; + line-height: 1.6; + } + + .type-grid { display: grid; gap: 12px; @@ -1350,6 +1402,14 @@这一课只练一件事:把重复或独立的判断抽成 ,用 传入数据,用 交回结果。完成四个小任务:选出 bool isEven(int x) 和 return x % 2 == 0,走一遍 isEven(4),再把闰年判断抽成函数。这里不会在浏览器里运行 C++。
diff --git a/lessons/s2-04/index.html b/lessons/s2-04/index.html index d21aa20..1a6fb65 100644 --- a/lessons/s2-04/index.html +++ b/lessons/s2-04/index.html @@ -991,6 +991,58 @@ background: rgba(24, 55, 47, 0.1); } + .prereq-note { + margin-top: 18px; + padding: 14px 16px; + border: 2px dashed rgba(24, 55, 47, 0.28); + border-radius: 6px; + background: rgba(255, 255, 255, 0.62); + } + + .prereq-note > strong { + display: block; + color: var(--forest); + font-family: "Songti SC", "STSong", serif; + font-size: 1rem; + } + + .prereq-list { + margin: 8px 0 0; + padding-left: 18px; + font-size: 0.78rem; + line-height: 1.65; + } + + .prereq-list li + li { + margin-top: 5px; + } + + .prereq-list span { + display: block; + color: rgba(24, 55, 47, 0.78); + } + + .prereq-link { + color: var(--forest); + font-weight: 800; + text-decoration: underline; + text-decoration-thickness: 2px; + text-underline-offset: 2px; + } + + .prereq-link:hover { + color: var(--orange); + } + + .prereq-path, + .prereq-empty { + margin: 9px 0 0; + color: rgba(24, 55, 47, 0.82); + font-size: 0.76rem; + line-height: 1.6; + } + + .type-grid { display: grid; gap: 12px; @@ -1350,6 +1402,14 @@这一课只练一件事:弄清变量的 ,看懂 改的是副本。完成四个小任务:选出 出了函数看不见,确认 addOne(n) 不会改外面的 n,再认识同名和 。这里不会在浏览器里运行 C++。
diff --git a/lessons/s2-05/index.html b/lessons/s2-05/index.html index b655e22..6376e5b 100644 --- a/lessons/s2-05/index.html +++ b/lessons/s2-05/index.html @@ -991,6 +991,58 @@ background: rgba(24, 55, 47, 0.1); } + .prereq-note { + margin-top: 18px; + padding: 14px 16px; + border: 2px dashed rgba(24, 55, 47, 0.28); + border-radius: 6px; + background: rgba(255, 255, 255, 0.62); + } + + .prereq-note > strong { + display: block; + color: var(--forest); + font-family: "Songti SC", "STSong", serif; + font-size: 1rem; + } + + .prereq-list { + margin: 8px 0 0; + padding-left: 18px; + font-size: 0.78rem; + line-height: 1.65; + } + + .prereq-list li + li { + margin-top: 5px; + } + + .prereq-list span { + display: block; + color: rgba(24, 55, 47, 0.78); + } + + .prereq-link { + color: var(--forest); + font-weight: 800; + text-decoration: underline; + text-decoration-thickness: 2px; + text-underline-offset: 2px; + } + + .prereq-link:hover { + color: var(--orange); + } + + .prereq-path, + .prereq-empty { + margin: 9px 0 0; + color: rgba(24, 55, 47, 0.82); + font-size: 0.76rem; + line-height: 1.6; + } + + .type-grid { display: grid; gap: 12px; @@ -1350,6 +1402,14 @@这一课只练一件事:用 把姓名、分数这些相关信息放进同一张记录卡。完成四个小任务:选出该用 struct 而不是一堆散变量,按顺序填写 stu.name 和 stu.score,再认识 和 。这里不会在浏览器里运行 C++。
diff --git a/lessons/s2-06/index.html b/lessons/s2-06/index.html index 2714db6..306b382 100644 --- a/lessons/s2-06/index.html +++ b/lessons/s2-06/index.html @@ -991,6 +991,58 @@ background: rgba(24, 55, 47, 0.1); } + .prereq-note { + margin-top: 18px; + padding: 14px 16px; + border: 2px dashed rgba(24, 55, 47, 0.28); + border-radius: 6px; + background: rgba(255, 255, 255, 0.62); + } + + .prereq-note > strong { + display: block; + color: var(--forest); + font-family: "Songti SC", "STSong", serif; + font-size: 1rem; + } + + .prereq-list { + margin: 8px 0 0; + padding-left: 18px; + font-size: 0.78rem; + line-height: 1.65; + } + + .prereq-list li + li { + margin-top: 5px; + } + + .prereq-list span { + display: block; + color: rgba(24, 55, 47, 0.78); + } + + .prereq-link { + color: var(--forest); + font-weight: 800; + text-decoration: underline; + text-decoration-thickness: 2px; + text-underline-offset: 2px; + } + + .prereq-link:hover { + color: var(--orange); + } + + .prereq-path, + .prereq-empty { + margin: 9px 0 0; + color: rgba(24, 55, 47, 0.82); + font-size: 0.76rem; + line-height: 1.6; + } + + .type-grid { display: grid; gap: 12px; @@ -1350,6 +1402,14 @@这一课只练一件事:用 决定谁该排前面,再用相邻 把数组排整齐。完成四个小任务:选出 ,走一遍 3, 1, 2,再认识 和 。这里不会在浏览器里运行 C++。
diff --git a/lessons/s2-07/index.html b/lessons/s2-07/index.html index 94f2e9f..999c226 100644 --- a/lessons/s2-07/index.html +++ b/lessons/s2-07/index.html @@ -994,6 +994,58 @@ background: rgba(24, 55, 47, 0.1); } + .prereq-note { + margin-top: 18px; + padding: 14px 16px; + border: 2px dashed rgba(24, 55, 47, 0.28); + border-radius: 6px; + background: rgba(255, 255, 255, 0.62); + } + + .prereq-note > strong { + display: block; + color: var(--forest); + font-family: "Songti SC", "STSong", serif; + font-size: 1rem; + } + + .prereq-list { + margin: 8px 0 0; + padding-left: 18px; + font-size: 0.78rem; + line-height: 1.65; + } + + .prereq-list li + li { + margin-top: 5px; + } + + .prereq-list span { + display: block; + color: rgba(24, 55, 47, 0.78); + } + + .prereq-link { + color: var(--forest); + font-weight: 800; + text-decoration: underline; + text-decoration-thickness: 2px; + text-underline-offset: 2px; + } + + .prereq-link:hover { + color: var(--orange); + } + + .prereq-path, + .prereq-empty { + margin: 9px 0 0; + color: rgba(24, 55, 47, 0.82); + font-size: 0.76rem; + line-height: 1.6; + } + + .type-grid { display: grid; gap: 12px; @@ -1353,6 +1405,14 @@这一课只练一件事:在有限的 里把每个可能都试一遍,并按题意做 。这就是 和 。完成四个小任务:选出范围,走一遍 1 到 3 数偶数,再对照 。这里不会在浏览器里运行 C++。
diff --git a/lessons/s2-08/index.html b/lessons/s2-08/index.html index 217d2ec..abca294 100644 --- a/lessons/s2-08/index.html +++ b/lessons/s2-08/index.html @@ -991,6 +991,58 @@ background: rgba(24, 55, 47, 0.1); } + .prereq-note { + margin-top: 18px; + padding: 14px 16px; + border: 2px dashed rgba(24, 55, 47, 0.28); + border-radius: 6px; + background: rgba(255, 255, 255, 0.62); + } + + .prereq-note > strong { + display: block; + color: var(--forest); + font-family: "Songti SC", "STSong", serif; + font-size: 1rem; + } + + .prereq-list { + margin: 8px 0 0; + padding-left: 18px; + font-size: 0.78rem; + line-height: 1.65; + } + + .prereq-list li + li { + margin-top: 5px; + } + + .prereq-list span { + display: block; + color: rgba(24, 55, 47, 0.78); + } + + .prereq-link { + color: var(--forest); + font-weight: 800; + text-decoration: underline; + text-decoration-thickness: 2px; + text-underline-offset: 2px; + } + + .prereq-link:hover { + color: var(--orange); + } + + .prereq-path, + .prereq-empty { + margin: 9px 0 0; + color: rgba(24, 55, 47, 0.82); + font-size: 0.76rem; + line-height: 1.6; + } + + .type-grid { display: grid; gap: 12px; @@ -1350,6 +1402,14 @@这一课只练一件事:先用 判断程序能不能跑完。把 记成大约 n 次,把 记成大约 n×n 次,再主动准备 。完成四个小任务:选出估法,数一遍 n=3,再对照 。这里不会在浏览器里运行 C++。
diff --git a/lessons/s3-01/index.html b/lessons/s3-01/index.html index b3a2ee5..195fcb5 100644 --- a/lessons/s3-01/index.html +++ b/lessons/s3-01/index.html @@ -991,6 +991,58 @@ background: rgba(24, 55, 47, 0.1); } + .prereq-note { + margin-top: 18px; + padding: 14px 16px; + border: 2px dashed rgba(24, 55, 47, 0.28); + border-radius: 6px; + background: rgba(255, 255, 255, 0.62); + } + + .prereq-note > strong { + display: block; + color: var(--forest); + font-family: "Songti SC", "STSong", serif; + font-size: 1rem; + } + + .prereq-list { + margin: 8px 0 0; + padding-left: 18px; + font-size: 0.78rem; + line-height: 1.65; + } + + .prereq-list li + li { + margin-top: 5px; + } + + .prereq-list span { + display: block; + color: rgba(24, 55, 47, 0.78); + } + + .prereq-link { + color: var(--forest); + font-weight: 800; + text-decoration: underline; + text-decoration-thickness: 2px; + text-underline-offset: 2px; + } + + .prereq-link:hover { + color: var(--orange); + } + + .prereq-path, + .prereq-empty { + margin: 9px 0 0; + color: rgba(24, 55, 47, 0.82); + font-size: 0.76rem; + line-height: 1.6; + } + + .type-grid { display: grid; gap: 12px; @@ -1350,6 +1402,14 @@这一课只练一件事:用 和 拆开 。个位是 x % 10,去掉个位是 x / 10,循环直到变成 0。同一套余数想法也能求 。完成四个小任务:选出拆法,走一遍 125,再看 GCD 与 。这里不会在浏览器里运行 C++。
diff --git a/lessons/s3-02/index.html b/lessons/s3-02/index.html index 0beb221..5872036 100644 --- a/lessons/s3-02/index.html +++ b/lessons/s3-02/index.html @@ -991,6 +991,58 @@ background: rgba(24, 55, 47, 0.1); } + .prereq-note { + margin-top: 18px; + padding: 14px 16px; + border: 2px dashed rgba(24, 55, 47, 0.28); + border-radius: 6px; + background: rgba(255, 255, 255, 0.62); + } + + .prereq-note > strong { + display: block; + color: var(--forest); + font-family: "Songti SC", "STSong", serif; + font-size: 1rem; + } + + .prereq-list { + margin: 8px 0 0; + padding-left: 18px; + font-size: 0.78rem; + line-height: 1.65; + } + + .prereq-list li + li { + margin-top: 5px; + } + + .prereq-list span { + display: block; + color: rgba(24, 55, 47, 0.78); + } + + .prereq-link { + color: var(--forest); + font-weight: 800; + text-decoration: underline; + text-decoration-thickness: 2px; + text-underline-offset: 2px; + } + + .prereq-link:hover { + color: var(--orange); + } + + .prereq-path, + .prereq-empty { + margin: 9px 0 0; + color: rgba(24, 55, 47, 0.82); + font-size: 0.76rem; + line-height: 1.6; + } + + .type-grid { display: grid; gap: 12px; @@ -1350,6 +1402,13 @@这一课只练一件事:把问题说成更小的同类问题,这就是 。先写 ,再写一次 。用 countdown(3) 追踪三层,把 想成一叠任务卡。建议掌握再用同样想法写 。完成四个小任务:选出拆法,走三层调用,再看边界与无限递归。这里不会在浏览器里运行 C++。
diff --git a/lessons/s3-03/index.html b/lessons/s3-03/index.html index 975f314..86c0db3 100644 --- a/lessons/s3-03/index.html +++ b/lessons/s3-03/index.html @@ -991,6 +991,58 @@ background: rgba(24, 55, 47, 0.1); } + .prereq-note { + margin-top: 18px; + padding: 14px 16px; + border: 2px dashed rgba(24, 55, 47, 0.28); + border-radius: 6px; + background: rgba(255, 255, 255, 0.62); + } + + .prereq-note > strong { + display: block; + color: var(--forest); + font-family: "Songti SC", "STSong", serif; + font-size: 1rem; + } + + .prereq-list { + margin: 8px 0 0; + padding-left: 18px; + font-size: 0.78rem; + line-height: 1.65; + } + + .prereq-list li + li { + margin-top: 5px; + } + + .prereq-list span { + display: block; + color: rgba(24, 55, 47, 0.78); + } + + .prereq-link { + color: var(--forest); + font-weight: 800; + text-decoration: underline; + text-decoration-thickness: 2px; + text-underline-offset: 2px; + } + + .prereq-link:hover { + color: var(--orange); + } + + .prereq-path, + .prereq-empty { + margin: 9px 0 0; + color: rgba(24, 55, 47, 0.82); + font-size: 0.76rem; + line-height: 1.6; + } + + .type-grid { display: grid; gap: 12px; @@ -1350,6 +1402,14 @@这一课只练一件事:在已经排好序、具有 的区间里做 。先确认能不能二分,再维护 ,每次看 丢掉一半。用 [1, 3, 5, 7, 9] 找 7 走三步,再测首尾答案,并看区间不缩小会变成 。对答案二分、lower_bound 细节和三分查找是拓展。这里不会在浏览器里运行 C++。
diff --git a/lessons/s3-04/index.html b/lessons/s3-04/index.html index 4bba75e..4450e80 100644 --- a/lessons/s3-04/index.html +++ b/lessons/s3-04/index.html @@ -991,6 +991,58 @@ background: rgba(24, 55, 47, 0.1); } + .prereq-note { + margin-top: 18px; + padding: 14px 16px; + border: 2px dashed rgba(24, 55, 47, 0.28); + border-radius: 6px; + background: rgba(255, 255, 255, 0.62); + } + + .prereq-note > strong { + display: block; + color: var(--forest); + font-family: "Songti SC", "STSong", serif; + font-size: 1rem; + } + + .prereq-list { + margin: 8px 0 0; + padding-left: 18px; + font-size: 0.78rem; + line-height: 1.65; + } + + .prereq-list li + li { + margin-top: 5px; + } + + .prereq-list span { + display: block; + color: rgba(24, 55, 47, 0.78); + } + + .prereq-link { + color: var(--forest); + font-weight: 800; + text-decoration: underline; + text-decoration-thickness: 2px; + text-underline-offset: 2px; + } + + .prereq-link:hover { + color: var(--orange); + } + + .prereq-path, + .prereq-empty { + margin: 9px 0 0; + color: rgba(24, 55, 47, 0.82); + font-size: 0.76rem; + line-height: 1.6; + } + + .type-grid { display: grid; gap: 12px; @@ -1350,6 +1402,14 @@这一课只练一件事:用 把连续一段加起来。先填 sum[],再查任意 :sum[r] - sum[l - 1]。用 a = [2, 5, 1, 4] 走三步,再处理从第一个元素开始,并看 漏掉 a[l] 会怎样。多次查询时预处理大约是 ,每次查询一次减法。二维前缀和、差分数组、树状数组是拓展。这里不会在浏览器里运行 C++。
diff --git a/lessons/s3-05/index.html b/lessons/s3-05/index.html index fdeda45..cfda77a 100644 --- a/lessons/s3-05/index.html +++ b/lessons/s3-05/index.html @@ -991,6 +991,58 @@ background: rgba(24, 55, 47, 0.1); } + .prereq-note { + margin-top: 18px; + padding: 14px 16px; + border: 2px dashed rgba(24, 55, 47, 0.28); + border-radius: 6px; + background: rgba(255, 255, 255, 0.62); + } + + .prereq-note > strong { + display: block; + color: var(--forest); + font-family: "Songti SC", "STSong", serif; + font-size: 1rem; + } + + .prereq-list { + margin: 8px 0 0; + padding-left: 18px; + font-size: 0.78rem; + line-height: 1.65; + } + + .prereq-list li + li { + margin-top: 5px; + } + + .prereq-list span { + display: block; + color: rgba(24, 55, 47, 0.78); + } + + .prereq-link { + color: var(--forest); + font-weight: 800; + text-decoration: underline; + text-decoration-thickness: 2px; + text-underline-offset: 2px; + } + + .prereq-link:hover { + color: var(--orange); + } + + .prereq-path, + .prereq-empty { + margin: 9px 0 0; + color: rgba(24, 55, 47, 0.82); + font-size: 0.76rem; + line-height: 1.6; + } + + .type-grid { display: grid; gap: 12px; @@ -1350,6 +1402,14 @@这一课只练一件事:用 维护一段连续区间。左边那个下标叫 left,右边那个叫 right,中间那一段就是 。先判断 成不成立,再决定移动哪一边。用 a = [1, 2, 3]、和不超过 3 走三步,并核对空窗口与满窗口。n 到 1e5 时,每个指针只往右走,大约是 ,比两层循环更能通过。单调队列、三数之和是拓展。这里不会在浏览器里运行 C++。
diff --git a/lessons/s3-06/index.html b/lessons/s3-06/index.html index 5fc897e..7d54759 100644 --- a/lessons/s3-06/index.html +++ b/lessons/s3-06/index.html @@ -991,6 +991,58 @@ background: rgba(24, 55, 47, 0.1); } + .prereq-note { + margin-top: 18px; + padding: 14px 16px; + border: 2px dashed rgba(24, 55, 47, 0.28); + border-radius: 6px; + background: rgba(255, 255, 255, 0.62); + } + + .prereq-note > strong { + display: block; + color: var(--forest); + font-family: "Songti SC", "STSong", serif; + font-size: 1rem; + } + + .prereq-list { + margin: 8px 0 0; + padding-left: 18px; + font-size: 0.78rem; + line-height: 1.65; + } + + .prereq-list li + li { + margin-top: 5px; + } + + .prereq-list span { + display: block; + color: rgba(24, 55, 47, 0.78); + } + + .prereq-link { + color: var(--forest); + font-weight: 800; + text-decoration: underline; + text-decoration-thickness: 2px; + text-underline-offset: 2px; + } + + .prereq-link:hover { + color: var(--orange); + } + + .prereq-path, + .prereq-empty { + margin: 9px 0 0; + color: rgba(24, 55, 47, 0.82); + font-size: 0.76rem; + line-height: 1.6; + } + + .type-grid { display: grid; gap: 12px; @@ -1350,6 +1402,14 @@这一课只练一件事:先定一条 的 ,再每一步按它选。选尽可能多的不重叠活动时,常用“结束最早的先选”。建议掌握:先 ,再 。必会还包括会找 :按时长最长选会失败。最小示例:美术 [1, 4]、书法 [1, 2]、围棋 [2, 5]。n 到 1e5 时,排序大约 n log n 再扫一遍,比枚举 2^n 更能通过。哈夫曼树、最短路是拓展。这里不会在浏览器里运行 C++。
diff --git a/lessons/s3-07/index.html b/lessons/s3-07/index.html index b64861a..ca71467 100644 --- a/lessons/s3-07/index.html +++ b/lessons/s3-07/index.html @@ -991,6 +991,58 @@ background: rgba(24, 55, 47, 0.1); } + .prereq-note { + margin-top: 18px; + padding: 14px 16px; + border: 2px dashed rgba(24, 55, 47, 0.28); + border-radius: 6px; + background: rgba(255, 255, 255, 0.62); + } + + .prereq-note > strong { + display: block; + color: var(--forest); + font-family: "Songti SC", "STSong", serif; + font-size: 1rem; + } + + .prereq-list { + margin: 8px 0 0; + padding-left: 18px; + font-size: 0.78rem; + line-height: 1.65; + } + + .prereq-list li + li { + margin-top: 5px; + } + + .prereq-list span { + display: block; + color: rgba(24, 55, 47, 0.78); + } + + .prereq-link { + color: var(--forest); + font-weight: 800; + text-decoration: underline; + text-decoration-thickness: 2px; + text-underline-offset: 2px; + } + + .prereq-link:hover { + color: var(--orange); + } + + .prereq-path, + .prereq-empty { + margin: 9px 0 0; + color: rgba(24, 55, 47, 0.82); + font-size: 0.76rem; + line-height: 1.6; + } + + .type-grid { display: grid; gap: 12px; @@ -1350,6 +1402,14 @@这一课只练一件事:分清 的 和 的 。必会:选对容器、完成 、说出入队出队顺序。最小示例:用栈检查 ([]);边界是空栈,以及错配 ([)]。建议掌握:队列适合一层一层往外看,这是 。n 个括号扫一遍大约 O(n),常见测试点能通过。图上的完整 BFS、最短路、单调栈是拓展。这里不会在浏览器里运行 C++。
diff --git a/lessons/s3-08/index.html b/lessons/s3-08/index.html index d5290cf..3a4428c 100644 --- a/lessons/s3-08/index.html +++ b/lessons/s3-08/index.html @@ -991,6 +991,58 @@ background: rgba(24, 55, 47, 0.1); } + .prereq-note { + margin-top: 18px; + padding: 14px 16px; + border: 2px dashed rgba(24, 55, 47, 0.28); + border-radius: 6px; + background: rgba(255, 255, 255, 0.62); + } + + .prereq-note > strong { + display: block; + color: var(--forest); + font-family: "Songti SC", "STSong", serif; + font-size: 1rem; + } + + .prereq-list { + margin: 8px 0 0; + padding-left: 18px; + font-size: 0.78rem; + line-height: 1.65; + } + + .prereq-list li + li { + margin-top: 5px; + } + + .prereq-list span { + display: block; + color: rgba(24, 55, 47, 0.78); + } + + .prereq-link { + color: var(--forest); + font-weight: 800; + text-decoration: underline; + text-decoration-thickness: 2px; + text-underline-offset: 2px; + } + + .prereq-link:hover { + color: var(--orange); + } + + .prereq-path, + .prereq-empty { + margin: 9px 0 0; + color: rgba(24, 55, 47, 0.82); + font-size: 0.76rem; + line-height: 1.6; + } + + .type-grid { display: grid; gap: 12px; @@ -1350,6 +1402,14 @@这一课只练一件事:用已经算好的小问题推出更大的问题,这叫 (C++ 里常用数组 dp)。必会:用一句话解释 dp[i]、写出 、用手算前三步验证 。最小示例:每次走 1 或 2 级台阶,求走到第 n 级的方法数。边界是忘了初值,以及 n=1。建议掌握:把答案记在 表里。填 n 格大约 O(n),常见测试点能通过。状态压缩、二维背包、树形 DP 是拓展。这里不会在浏览器里运行 C++。
diff --git a/lessons/s4-01/index.html b/lessons/s4-01/index.html index 07bd23a..c95ecbc 100644 --- a/lessons/s4-01/index.html +++ b/lessons/s4-01/index.html @@ -991,6 +991,58 @@ background: rgba(24, 55, 47, 0.1); } + .prereq-note { + margin-top: 18px; + padding: 14px 16px; + border: 2px dashed rgba(24, 55, 47, 0.28); + border-radius: 6px; + background: rgba(255, 255, 255, 0.62); + } + + .prereq-note > strong { + display: block; + color: var(--forest); + font-family: "Songti SC", "STSong", serif; + font-size: 1rem; + } + + .prereq-list { + margin: 8px 0 0; + padding-left: 18px; + font-size: 0.78rem; + line-height: 1.65; + } + + .prereq-list li + li { + margin-top: 5px; + } + + .prereq-list span { + display: block; + color: rgba(24, 55, 47, 0.78); + } + + .prereq-link { + color: var(--forest); + font-weight: 800; + text-decoration: underline; + text-decoration-thickness: 2px; + text-underline-offset: 2px; + } + + .prereq-link:hover { + color: var(--orange); + } + + .prereq-path, + .prereq-empty { + margin: 9px 0 0; + color: rgba(24, 55, 47, 0.82); + font-size: 0.76rem; + line-height: 1.6; + } + + .type-grid { display: grid; gap: 12px; @@ -1350,6 +1402,14 @@这一课只练一件事:按竞赛题面交一份干净程序,这叫 。必会:圈出 、删掉提示语、按 独立核对。最小示例:读入整数 n,输出 n+1;范围 1 <= n <= 1000,也就是 n <= 1000;样例输入 3,输出 4。边界是多写了“请输入”,以及忘了对照样例。建议掌握:cin/cout 接到 。评测机内部和 freopen 文件读写是拓展。这里不会在浏览器里运行 C++。
diff --git a/lessons/s4-02/index.html b/lessons/s4-02/index.html index dd835db..e6d82c1 100644 --- a/lessons/s4-02/index.html +++ b/lessons/s4-02/index.html @@ -991,6 +991,58 @@ background: rgba(24, 55, 47, 0.1); } + .prereq-note { + margin-top: 18px; + padding: 14px 16px; + border: 2px dashed rgba(24, 55, 47, 0.28); + border-radius: 6px; + background: rgba(255, 255, 255, 0.62); + } + + .prereq-note > strong { + display: block; + color: var(--forest); + font-family: "Songti SC", "STSong", serif; + font-size: 1rem; + } + + .prereq-list { + margin: 8px 0 0; + padding-left: 18px; + font-size: 0.78rem; + line-height: 1.65; + } + + .prereq-list li + li { + margin-top: 5px; + } + + .prereq-list span { + display: block; + color: rgba(24, 55, 47, 0.78); + } + + .prereq-link { + color: var(--forest); + font-weight: 800; + text-decoration: underline; + text-decoration-thickness: 2px; + text-underline-offset: 2px; + } + + .prereq-link:hover { + color: var(--orange); + } + + .prereq-path, + .prereq-empty { + margin: 9px 0 0; + color: rgba(24, 55, 47, 0.82); + font-size: 0.76rem; + line-height: 1.6; + } + + .type-grid { display: grid; gap: 12px; @@ -1350,6 +1402,14 @@这一课只练一件事:把题意拆成 ,再动手写程序。必会:分清 、用 、写出两个边界。最小示例:读入 n 和 n 个整数,输出它们的和; 1 <= n <= 1000;样例输入 3 和 1 2 3,输出 6,也就是 1+2+3=6。边界是 n=1,以及全是 0。建议掌握:先列 。图论、搜索树、状态压缩是拓展。这里不会在浏览器里运行 C++。
diff --git a/lessons/s4-03/index.html b/lessons/s4-03/index.html index 1ded1eb..01ceb58 100644 --- a/lessons/s4-03/index.html +++ b/lessons/s4-03/index.html @@ -991,6 +991,58 @@ background: rgba(24, 55, 47, 0.1); } + .prereq-note { + margin-top: 18px; + padding: 14px 16px; + border: 2px dashed rgba(24, 55, 47, 0.28); + border-radius: 6px; + background: rgba(255, 255, 255, 0.62); + } + + .prereq-note > strong { + display: block; + color: var(--forest); + font-family: "Songti SC", "STSong", serif; + font-size: 1rem; + } + + .prereq-list { + margin: 8px 0 0; + padding-left: 18px; + font-size: 0.78rem; + line-height: 1.65; + } + + .prereq-list li + li { + margin-top: 5px; + } + + .prereq-list span { + display: block; + color: rgba(24, 55, 47, 0.78); + } + + .prereq-link { + color: var(--forest); + font-weight: 800; + text-decoration: underline; + text-decoration-thickness: 2px; + text-underline-offset: 2px; + } + + .prereq-link:hover { + color: var(--orange); + } + + .prereq-path, + .prereq-empty { + margin: 9px 0 0; + color: rgba(24, 55, 47, 0.82); + font-size: 0.76rem; + line-height: 1.6; + } + + .type-grid { display: grid; gap: 12px; @@ -1350,6 +1402,14 @@这一课只练一件事:先画 ,再按 逐步处理。必会:判断每一步的 、用 核对结果、写出两个边界。最小示例:存钱罐模拟,读入 n 次操作,正数表示存入,负数表示取出的金额;1 <= n <= 1000;余额不足时跳过该次取款。样例输入 3 次操作 +5 -3 -10,追踪过程 5 → 2 → 跳过,输出余额 2、跳过 1 次。边界是 n=1,以及全部因 不足被跳过。建议掌握:统计跳过次数。多账户、多币种、复杂利率是拓展。这里不会在浏览器里运行 C++。
diff --git a/lessons/s4-04/index.html b/lessons/s4-04/index.html index b6cb2dc..20aaa83 100644 --- a/lessons/s4-04/index.html +++ b/lessons/s4-04/index.html @@ -991,6 +991,58 @@ background: rgba(24, 55, 47, 0.1); } + .prereq-note { + margin-top: 18px; + padding: 14px 16px; + border: 2px dashed rgba(24, 55, 47, 0.28); + border-radius: 6px; + background: rgba(255, 255, 255, 0.62); + } + + .prereq-note > strong { + display: block; + color: var(--forest); + font-family: "Songti SC", "STSong", serif; + font-size: 1rem; + } + + .prereq-list { + margin: 8px 0 0; + padding-left: 18px; + font-size: 0.78rem; + line-height: 1.65; + } + + .prereq-list li + li { + margin-top: 5px; + } + + .prereq-list span { + display: block; + color: rgba(24, 55, 47, 0.78); + } + + .prereq-link { + color: var(--forest); + font-weight: 800; + text-decoration: underline; + text-decoration-thickness: 2px; + text-underline-offset: 2px; + } + + .prereq-link:hover { + color: var(--orange); + } + + .prereq-path, + .prereq-empty { + margin: 9px 0 0; + color: rgba(24, 55, 47, 0.82); + font-size: 0.76rem; + line-height: 1.6; + } + + .type-grid { display: grid; gap: 12px; @@ -1350,6 +1402,14 @@这一课只练一件事:把每一步的选择画成 ,用 逐条走完。必会:判断安全的 条件、写完一条分支后正确 、写出停止搜索的边界。最小示例:从 3 个数 1、2、3 中选出若干个,使它们的和恰好等于 target = 4;每个数只能选或不选,1 <= n <= 20。样例追踪:选 1、跳过 2、选 3,和为 1 + 3 = 4,命中后记录方案并回溯,继续尝试其他组合。边界是 n=1(只判断这一个数是否等于 target),以及所有数都试完仍未凑出 target(无解,回溯到底)。建议掌握:统计一共找到几种方案。多维状态剪枝、双向搜索、记忆化是拓展。这里不会在浏览器里运行 C++。
diff --git a/lessons/s4-05/index.html b/lessons/s4-05/index.html index 3f7acdf..7c9493d 100644 --- a/lessons/s4-05/index.html +++ b/lessons/s4-05/index.html @@ -991,6 +991,58 @@ background: rgba(24, 55, 47, 0.1); } + .prereq-note { + margin-top: 18px; + padding: 14px 16px; + border: 2px dashed rgba(24, 55, 47, 0.28); + border-radius: 6px; + background: rgba(255, 255, 255, 0.62); + } + + .prereq-note > strong { + display: block; + color: var(--forest); + font-family: "Songti SC", "STSong", serif; + font-size: 1rem; + } + + .prereq-list { + margin: 8px 0 0; + padding-left: 18px; + font-size: 0.78rem; + line-height: 1.65; + } + + .prereq-list li + li { + margin-top: 5px; + } + + .prereq-list span { + display: block; + color: rgba(24, 55, 47, 0.78); + } + + .prereq-link { + color: var(--forest); + font-weight: 800; + text-decoration: underline; + text-decoration-thickness: 2px; + text-underline-offset: 2px; + } + + .prereq-link:hover { + color: var(--orange); + } + + .prereq-path, + .prereq-empty { + margin: 9px 0 0; + color: rgba(24, 55, 47, 0.82); + font-size: 0.76rem; + line-height: 1.6; + } + + .type-grid { display: grid; gap: 12px; @@ -1350,6 +1402,14 @@这一课只练一件事:拿到一批数据后,先看清 ,再决定用、先后扫描,还是复用。必会:判断何时该用排序、处理好、按取值范围选出数组或映射方案。最小示例:6 名学生的成绩 90 78 85 90 85 90,分数范围 0 <= 分数 <= 100,1 <= n <= 1000,要求统计每个分数出现次数,并输出出现次数最多的分数。样例追踪:排序后是 78 85 85 90 90 90,扫描一遍数出 78 出现 1 次、85 出现 2 次、90 出现 3 次,最高频分数是 90。边界是 n=1(这唯一一个分数本身就是最高频,次数为 1),以及多个分数并列最高频时按“取数值更小的分数”统一规则。建议掌握:分数范围很小(0~100)时可以直接开计数数组,比排序更省事。范围变得很大(比如到 10 的 9 次方)时改用映射结构是拓展。这里不会在浏览器里运行 C++。
diff --git a/lessons/s4-06/index.html b/lessons/s4-06/index.html index ebac562..0ed9863 100644 --- a/lessons/s4-06/index.html +++ b/lessons/s4-06/index.html @@ -991,6 +991,58 @@ background: rgba(24, 55, 47, 0.1); } + .prereq-note { + margin-top: 18px; + padding: 14px 16px; + border: 2px dashed rgba(24, 55, 47, 0.28); + border-radius: 6px; + background: rgba(255, 255, 255, 0.62); + } + + .prereq-note > strong { + display: block; + color: var(--forest); + font-family: "Songti SC", "STSong", serif; + font-size: 1rem; + } + + .prereq-list { + margin: 8px 0 0; + padding-left: 18px; + font-size: 0.78rem; + line-height: 1.65; + } + + .prereq-list li + li { + margin-top: 5px; + } + + .prereq-list span { + display: block; + color: rgba(24, 55, 47, 0.78); + } + + .prereq-link { + color: var(--forest); + font-weight: 800; + text-decoration: underline; + text-decoration-thickness: 2px; + text-underline-offset: 2px; + } + + .prereq-link:hover { + color: var(--orange); + } + + .prereq-path, + .prereq-empty { + margin: 9px 0 0; + color: rgba(24, 55, 47, 0.82); + font-size: 0.76rem; + line-height: 1.6; + } + + .type-grid { display: grid; gap: 12px; @@ -1350,6 +1402,13 @@这一课只练一件事:遇到“不能选相邻两个位置、要最大化总和”这类问题时,用 dp[i] 表示到第 i 个位置为止能拿到的最大总和,用写出转移式子,并确认这是而不是靠感觉贪心。必会:能列出状态表、能写出转移式子、能检查不可达状态(比如 i-2 不存在时不能越界读取,要按已给的初值处理)。最小示例:4 个位置的金额 3 7 4 6,规则是不能选相邻两个位置,求能拿到的最大总和,1 <= n <= 1000。样例追踪:dp[0] = 3,dp[1] = max(3, 7) = 7,dp[2] = max(dp[1], dp[0]+4) = max(7, 7) = 7,dp[3] = max(dp[2], dp[1]+6) = max(7, 13) = 13,所以 dp[3] = 13。边界是 n=1(只有一个位置,直接选它)和不可达状态(i-2 越界时不能凭空访问,要靠 dp[0]、dp[1] 的初值兜底)。建议掌握:这比枚举所有选法(2 的 n 次方种)快得多,n 变大时只有枚举才会超时。拓展:如果 dp[i] 只依赖前两个状态,可以用把整个数组换成两个变量,现在只需要认识这个概念,不强制要求会写。这里不会在浏览器里运行 C++。
diff --git a/lessons/s4-07/index.html b/lessons/s4-07/index.html index 74aa244..5d30ebb 100644 --- a/lessons/s4-07/index.html +++ b/lessons/s4-07/index.html @@ -991,6 +991,58 @@ background: rgba(24, 55, 47, 0.1); } + .prereq-note { + margin-top: 18px; + padding: 14px 16px; + border: 2px dashed rgba(24, 55, 47, 0.28); + border-radius: 6px; + background: rgba(255, 255, 255, 0.62); + } + + .prereq-note > strong { + display: block; + color: var(--forest); + font-family: "Songti SC", "STSong", serif; + font-size: 1rem; + } + + .prereq-list { + margin: 8px 0 0; + padding-left: 18px; + font-size: 0.78rem; + line-height: 1.65; + } + + .prereq-list li + li { + margin-top: 5px; + } + + .prereq-list span { + display: block; + color: rgba(24, 55, 47, 0.78); + } + + .prereq-link { + color: var(--forest); + font-weight: 800; + text-decoration: underline; + text-decoration-thickness: 2px; + text-underline-offset: 2px; + } + + .prereq-link:hover { + color: var(--orange); + } + + .prereq-path, + .prereq-empty { + margin: 9px 0 0; + color: rgba(24, 55, 47, 0.82); + font-size: 0.76rem; + line-height: 1.6; + } + + .type-grid { display: grid; gap: 12px; @@ -1350,6 +1402,14 @@这一课只练一件事:遇到“若干个点、若干条边,问一共分成几组互相连通”这类问题时,用把点和边画出来,用记录每个点连着谁,再用防止重复访问,跑一次或数出数量。必会:能画出小图和邻接关系、能写访问标记、能说明 DFS 与 BFS 的顺序差异。最小示例:5 个点(编号 0~4),边为 (0,1) (1,2) (3,4),这是无向图,1 <= n <= 1000。样例追踪:邻接表 adj[0]={1}, adj[1]={0,2}, adj[2]={1}, adj[3]={4}, adj[4]={3};访问标记 visited 全部初始化为 false;从 0 出发 DFS 访问到 {0,1,2},再从还没访问过的 3 出发 DFS 访问到 {3,4},连通块数 = 2。边界是没有边的孤立点(自己就是一个连通块)和已经访问过的点(不能再重复计数)。建议掌握:DFS 沿一条路径尽量走到底再回头,BFS 按“一层一层”向外扩展,得到的是离起点的最短步数层级,这两种顺序本质不同。拓展:BFS 求出的层数可以直接当作“最短步数”的初步认识,现在只需要知道概念,不强制要求现在就写完整的最短路模板。这里不会在浏览器里运行 C++。
diff --git a/lessons/s4-08/index.html b/lessons/s4-08/index.html index b9a8001..9ac820b 100644 --- a/lessons/s4-08/index.html +++ b/lessons/s4-08/index.html @@ -991,6 +991,58 @@ background: rgba(24, 55, 47, 0.1); } + .prereq-note { + margin-top: 18px; + padding: 14px 16px; + border: 2px dashed rgba(24, 55, 47, 0.28); + border-radius: 6px; + background: rgba(255, 255, 255, 0.62); + } + + .prereq-note > strong { + display: block; + color: var(--forest); + font-family: "Songti SC", "STSong", serif; + font-size: 1rem; + } + + .prereq-list { + margin: 8px 0 0; + padding-left: 18px; + font-size: 0.78rem; + line-height: 1.65; + } + + .prereq-list li + li { + margin-top: 5px; + } + + .prereq-list span { + display: block; + color: rgba(24, 55, 47, 0.78); + } + + .prereq-link { + color: var(--forest); + font-weight: 800; + text-decoration: underline; + text-decoration-thickness: 2px; + text-underline-offset: 2px; + } + + .prereq-link:hover { + color: var(--orange); + } + + .prereq-path, + .prereq-empty { + margin: 9px 0 0; + color: rgba(24, 55, 47, 0.82); + font-size: 0.76rem; + line-height: 1.6; + } + + .type-grid { display: grid; gap: 12px; @@ -1350,6 +1402,14 @@这一课只练一件事:题目按数据范围拆成多个时,如何在有限时间里稳稳拿到。做法是先写一份能过小数据的,再按的顺序挑战更难的子任务;写不出最优算法时,可以先用的简单实现去覆盖数据较小的子任务。必会:能圈出每个子任务的数据范围、能先写保底方案、能安排提交前的检查时间。最小示例:一道满分 100 分的题拆成两个子任务——子任务 1(1 <= n <= 1000,30 分,暴力 O(n²) 可过)、子任务 2(1 <= n <= 100000,70 分,需要 O(n log n))。你目前只会写 O(n²) 暴力。样例追踪:先写暴力覆盖子任务 1,本地用小数据测试通过,预计至少拿到 30 分;再尝试把算法优化到 O(n log n) 冲子任务 2 的 70 分;如果优化没调完,仍然提交能跑的暴力版本,保住已经确定的 30 分。边界是不能因为一心冲满分而不写任何代码(0 分)、也不能在合并优化代码时把还能跑的保底方案改坏。建议掌握:先完成有把握、分值明确的子任务,再挑战更难的子任务,不要把全部时间押在最难的部分。拓展:复杂度降级只是拓展技巧——用更简单但正确的实现去换取小数据子任务的分数,不强制现在就会写出满分算法。这里不会在浏览器里运行 C++。
diff --git a/lessons/s5-01/index.html b/lessons/s5-01/index.html index 5fcbb71..b81123c 100644 --- a/lessons/s5-01/index.html +++ b/lessons/s5-01/index.html @@ -991,6 +991,58 @@ background: rgba(24, 55, 47, 0.1); } + .prereq-note { + margin-top: 18px; + padding: 14px 16px; + border: 2px dashed rgba(24, 55, 47, 0.28); + border-radius: 6px; + background: rgba(255, 255, 255, 0.62); + } + + .prereq-note > strong { + display: block; + color: var(--forest); + font-family: "Songti SC", "STSong", serif; + font-size: 1rem; + } + + .prereq-list { + margin: 8px 0 0; + padding-left: 18px; + font-size: 0.78rem; + line-height: 1.65; + } + + .prereq-list li + li { + margin-top: 5px; + } + + .prereq-list span { + display: block; + color: rgba(24, 55, 47, 0.78); + } + + .prereq-link { + color: var(--forest); + font-weight: 800; + text-decoration: underline; + text-decoration-thickness: 2px; + text-underline-offset: 2px; + } + + .prereq-link:hover { + color: var(--orange); + } + + .prereq-path, + .prereq-empty { + margin: 9px 0 0; + color: rgba(24, 55, 47, 0.82); + font-size: 0.76rem; + line-height: 1.6; + } + + .type-grid { display: grid; gap: 12px; @@ -1350,6 +1402,14 @@这一课只练一件事:在比赛时间里,按固定的从读题走到提交。顺序是、写、编码、做,并。必会:能在限时内写一张流程卡、能预留检查时间、不跳过样例。最小示例:一次 40 分钟训练只做一道题。样例追踪:第 1 到 5 分钟审题并记下时间预算;第 6 到 10 分钟写下已知、未知和打算;第 11 到 32 分钟编码;第 33 到 36 分钟对照样例;最后 4 分钟做提交前检查。边界是不能拿到题就直接敲代码,也不能样例还没对照就提交。建议掌握:编码前先留下建模草稿,哪怕只有三行。拓展:把 40 分钟再拆成更细的分钟级时间片只是拓展习惯,现在不当硬门槛。这里不会在浏览器里运行 C++。
diff --git a/lessons/s5-02/index.html b/lessons/s5-02/index.html index 2d62c9d..33d0f37 100644 --- a/lessons/s5-02/index.html +++ b/lessons/s5-02/index.html @@ -991,6 +991,58 @@ background: rgba(24, 55, 47, 0.1); } + .prereq-note { + margin-top: 18px; + padding: 14px 16px; + border: 2px dashed rgba(24, 55, 47, 0.28); + border-radius: 6px; + background: rgba(255, 255, 255, 0.62); + } + + .prereq-note > strong { + display: block; + color: var(--forest); + font-family: "Songti SC", "STSong", serif; + font-size: 1rem; + } + + .prereq-list { + margin: 8px 0 0; + padding-left: 18px; + font-size: 0.78rem; + line-height: 1.65; + } + + .prereq-list li + li { + margin-top: 5px; + } + + .prereq-list span { + display: block; + color: rgba(24, 55, 47, 0.78); + } + + .prereq-link { + color: var(--forest); + font-weight: 800; + text-decoration: underline; + text-decoration-thickness: 2px; + text-underline-offset: 2px; + } + + .prereq-link:hover { + color: var(--orange); + } + + .prereq-path, + .prereq-empty { + margin: 9px 0 0; + color: rgba(24, 55, 47, 0.82); + font-size: 0.76rem; + line-height: 1.6; + } + + .type-grid { display: grid; gap: 12px; @@ -1350,6 +1402,14 @@这一课只练一件事:用按证据修错,而不是靠猜测乱改。顺序是先做,再用或检验假设,最后记下修复原因并重新测试。必会:能把错误缩小到小数据;能写出验证假设的输出;不靠乱改碰运气。最小示例:求前缀和的程序对 n=5 错了,先缩成 n=2 仍错。样例追踪:n=5 输出 9,手算应是 10;缩到 a=[1,2] 仍少 1;分段输出发现 prefix 从 a[1] 开始加。边界是不要在大数据上猜改,也不要改完不回归。建议掌握:写下修复原因。拓展:用暴力程序做只是拓展习惯,现在不当硬门槛。这里不会在浏览器里运行 C++。
diff --git a/lessons/s5-03/index.html b/lessons/s5-03/index.html index aa962ef..cb9e210 100644 --- a/lessons/s5-03/index.html +++ b/lessons/s5-03/index.html @@ -991,6 +991,58 @@ background: rgba(24, 55, 47, 0.1); } + .prereq-note { + margin-top: 18px; + padding: 14px 16px; + border: 2px dashed rgba(24, 55, 47, 0.28); + border-radius: 6px; + background: rgba(255, 255, 255, 0.62); + } + + .prereq-note > strong { + display: block; + color: var(--forest); + font-family: "Songti SC", "STSong", serif; + font-size: 1rem; + } + + .prereq-list { + margin: 8px 0 0; + padding-left: 18px; + font-size: 0.78rem; + line-height: 1.65; + } + + .prereq-list li + li { + margin-top: 5px; + } + + .prereq-list span { + display: block; + color: rgba(24, 55, 47, 0.78); + } + + .prereq-link { + color: var(--forest); + font-weight: 800; + text-decoration: underline; + text-decoration-thickness: 2px; + text-underline-offset: 2px; + } + + .prereq-link:hover { + color: var(--orange); + } + + .prereq-path, + .prereq-empty { + margin: 9px 0 0; + color: rgba(24, 55, 47, 0.82); + font-size: 0.76rem; + line-height: 1.6; + } + + .type-grid { display: grid; gap: 12px; @@ -1350,6 +1402,14 @@这一课只练一件事:看到一段快出错的代码,先说出它属于哪一类语言层面的失分点,再选出正确修法。五类陷阱是、、、与混淆、以及没删干净的调试输出。必会:能识别这五类陷阱;能为大乘积选择;能检查数组访问是不是在 0 到 n-1 之内。最小示例:`int total = 50000 * 50000;` 编译能过,但 total 变成负数——这是溢出,不是越界。边界是范围表和精确位数是拓展,现在只要求先看规模。建议掌握:提交前删掉之外的调试文字并再测一遍。拓展:把五类陷阱整理成赛前检查清单,只是习惯,不是新语法。这里不会在浏览器里运行 C++。
diff --git a/lessons/s5-04/index.html b/lessons/s5-04/index.html index 065f684..67409d2 100644 --- a/lessons/s5-04/index.html +++ b/lessons/s5-04/index.html @@ -991,6 +991,58 @@ background: rgba(24, 55, 47, 0.1); } + .prereq-note { + margin-top: 18px; + padding: 14px 16px; + border: 2px dashed rgba(24, 55, 47, 0.28); + border-radius: 6px; + background: rgba(255, 255, 255, 0.62); + } + + .prereq-note > strong { + display: block; + color: var(--forest); + font-family: "Songti SC", "STSong", serif; + font-size: 1rem; + } + + .prereq-list { + margin: 8px 0 0; + padding-left: 18px; + font-size: 0.78rem; + line-height: 1.65; + } + + .prereq-list li + li { + margin-top: 5px; + } + + .prereq-list span { + display: block; + color: rgba(24, 55, 47, 0.78); + } + + .prereq-link { + color: var(--forest); + font-weight: 800; + text-decoration: underline; + text-decoration-thickness: 2px; + text-underline-offset: 2px; + } + + .prereq-link:hover { + color: var(--orange); + } + + .prereq-path, + .prereq-empty { + margin: 9px 0 0; + color: rgba(24, 55, 47, 0.82); + font-size: 0.76rem; + line-height: 1.6; + } + + .type-grid { display: grid; gap: 12px; @@ -1350,6 +1402,14 @@这一课只练一件事:遇到一道暂时写不出满分算法的难题时,先写一份能验证的,再逐步升级,而不是一步到位地追求最优解。做法是:先用最直接的写法覆盖小数据,标注它能处理的范围和会漏掉的,再动手做,同时用确认没有引入。必会:能先写小数据可过的基线;能标注它的适用范围和已知会出错的特殊情况;能避免把破坏正确性的“捷径”当成优化。最小示例:区间求和题,q 次查询每次给出 l, r,求 a[l..r] 的和。基线写法是每次查询都从 l 循环到 r 直接累加,复杂度 O(n·q),在 n、q 都不大时能过。样例追踪:先写这份基线并用样例测试通过;标注 l > r 是空区间应输出 0、l == r 是单点区间;再引入前缀和 sum[i] = sum[i-1] + a[i],把每次查询变成 sum[r] - sum[l-1],复杂度升级为 O(n+q);升级后用基线在小数据上对拍,确认输出完全一致。边界是不能跳过基线直接想前缀和公式、也不能为了让代码“看起来更快”而删掉必要的下标或判断。建议掌握:升级前先标好基线的适用范围,升级后立刻用基线做一次对拍,而不是假设新写法一定正确。拓展:伪优化是指看起来精简了代码、实际上悄悄改变了结果的“假优化”,现在只需要能识别一个例子。这里不会在浏览器里运行 C++。
diff --git a/lessons/s5-05/index.html b/lessons/s5-05/index.html index aa8f534..238d2d3 100644 --- a/lessons/s5-05/index.html +++ b/lessons/s5-05/index.html @@ -991,6 +991,58 @@ background: rgba(24, 55, 47, 0.1); } + .prereq-note { + margin-top: 18px; + padding: 14px 16px; + border: 2px dashed rgba(24, 55, 47, 0.28); + border-radius: 6px; + background: rgba(255, 255, 255, 0.62); + } + + .prereq-note > strong { + display: block; + color: var(--forest); + font-family: "Songti SC", "STSong", serif; + font-size: 1rem; + } + + .prereq-list { + margin: 8px 0 0; + padding-left: 18px; + font-size: 0.78rem; + line-height: 1.65; + } + + .prereq-list li + li { + margin-top: 5px; + } + + .prereq-list span { + display: block; + color: rgba(24, 55, 47, 0.78); + } + + .prereq-link { + color: var(--forest); + font-weight: 800; + text-decoration: underline; + text-decoration-thickness: 2px; + text-underline-offset: 2px; + } + + .prereq-link:hover { + color: var(--orange); + } + + .prereq-path, + .prereq-empty { + margin: 9px 0 0; + color: rgba(24, 55, 47, 0.82); + font-size: 0.76rem; + line-height: 1.6; + } + + .type-grid { display: grid; gap: 12px; @@ -1350,6 +1402,14 @@这一课只练一件事:在一次限时的里,先把几道题的排好,再在写完或放弃每一题之后,用如实记录测过的数据和结果,并靠判断要不要换题或提前收尾。必会:能在模拟赛开始前用几分钟浏览全部题目并排出提交顺序(有把握的先做);能在完成或放弃一题后,在自测表里如实记录测试数据、结果和花费时间。最小示例:本次模拟赛有 3 道题——T1(简单,全对预计 20 分)、T2(中等,部分测试点可拿分)、T3(难题,暂时只会写暴力),总时长 120 分钟。样例追踪:先用 5 分钟浏览三题,标出把握程度,排出顺序 T1 → T2 → T3;写完 T1 后在自测表记录“样例通过、n=0 边界也测过,用时 25 分钟”;转到 T2,写完后自测表记录“样例通过,n=0 尚未测”;剩余时间不够时不硬啃 T3,先回头补测 T2 的边界。边界是不能跳过浏览直接从第一题硬写到底、也不能为了赶时间就跳过自测表不记录。建议掌握:定期查看时间记录,判断是否该换题或提前收尾,先保住已经确定的分数。拓展:某组数据没时间测完时,要在自测表里如实写清没测的原因(例如“时间不够”),供考后复盘,而不是假装测过或直接删掉那一行。这里不会在浏览器里运行 C++。
diff --git a/lessons/s5-06/index.html b/lessons/s5-06/index.html index 74e5820..43bf659 100644 --- a/lessons/s5-06/index.html +++ b/lessons/s5-06/index.html @@ -991,6 +991,58 @@ background: rgba(24, 55, 47, 0.1); } + .prereq-note { + margin-top: 18px; + padding: 14px 16px; + border: 2px dashed rgba(24, 55, 47, 0.28); + border-radius: 6px; + background: rgba(255, 255, 255, 0.62); + } + + .prereq-note > strong { + display: block; + color: var(--forest); + font-family: "Songti SC", "STSong", serif; + font-size: 1rem; + } + + .prereq-list { + margin: 8px 0 0; + padding-left: 18px; + font-size: 0.78rem; + line-height: 1.65; + } + + .prereq-list li + li { + margin-top: 5px; + } + + .prereq-list span { + display: block; + color: rgba(24, 55, 47, 0.78); + } + + .prereq-link { + color: var(--forest); + font-weight: 800; + text-decoration: underline; + text-decoration-thickness: 2px; + text-underline-offset: 2px; + } + + .prereq-link:hover { + color: var(--orange); + } + + .prereq-path, + .prereq-empty { + margin: 9px 0 0; + color: rgba(24, 55, 47, 0.82); + font-size: 0.76rem; + line-height: 1.6; + } + + .type-grid { display: grid; gap: 12px; @@ -1350,6 +1402,14 @@这一课只练一件事:模拟赛结束、看到失分之后,先做——判断这处失分属于读题错(看漏或看错题目条件)、算法错(方法本身不对)、实现错(思路对但代码写错)、(没测最小或最大等边界)还是时间错(没能在规定时间内完成)中的哪一种;再给这一类写一条;最后挑一道用得上这条改法的题一遍,验证改法是否真的有效。必会:能把每处失分分类到五种错误之一;能给每一类写一条具体、下次能照做的改法。最小示例:模拟赛的 T2 提交后只过了 40% 的测试点——重新跑一遍样例和几组边界数据后发现,样例和 n=1 都正确,但 n 取到题目允许的最大值时输出少了最后一个元素;定位到代码里数组下标从 1 开始却写成了从 0 开始,最后一个元素被漏算。这属于实现错(思路没问题,是写代码时的具体失误),改法是“写循环前先在纸上画出下标范围,和数组大小对一遍”,然后挑一道用到同类下标的题重做一次,确认这次没有再漏算。边界是不能把说不清原因的失分笼统地归为“粗心”,也不能写完分类就结束、不落到具体改法上。建议掌握:时间不够复盘完所有题时,优先给丢分最多的几道题分类、写改法。拓展:拿不准一处失分具体属于哪一类时,要如实标注“暂不确定”,留到下次复盘或请教时再确认,而不是随便选一个类别凑数。这里不会在浏览器里运行 C++。
diff --git a/lessons/s5-07/index.html b/lessons/s5-07/index.html index d8a7082..1a9ad85 100644 --- a/lessons/s5-07/index.html +++ b/lessons/s5-07/index.html @@ -991,6 +991,58 @@ background: rgba(24, 55, 47, 0.1); } + .prereq-note { + margin-top: 18px; + padding: 14px 16px; + border: 2px dashed rgba(24, 55, 47, 0.28); + border-radius: 6px; + background: rgba(255, 255, 255, 0.62); + } + + .prereq-note > strong { + display: block; + color: var(--forest); + font-family: "Songti SC", "STSong", serif; + font-size: 1rem; + } + + .prereq-list { + margin: 8px 0 0; + padding-left: 18px; + font-size: 0.78rem; + line-height: 1.65; + } + + .prereq-list li + li { + margin-top: 5px; + } + + .prereq-list span { + display: block; + color: rgba(24, 55, 47, 0.78); + } + + .prereq-link { + color: var(--forest); + font-weight: 800; + text-decoration: underline; + text-decoration-thickness: 2px; + text-underline-offset: 2px; + } + + .prereq-link:hover { + color: var(--orange); + } + + .prereq-path, + .prereq-empty { + margin: 9px 0 0; + color: rgba(24, 55, 47, 0.82); + font-size: 0.76rem; + line-height: 1.6; + } + + .type-grid { display: grid; gap: 12px; @@ -1350,6 +1402,14 @@这一课只练一件事:用一组独立完成第二次模拟赛,再把这次的记录和第一次(S5-05)的自测表、时间记录做,看时间分配和错误有没有真的变化,最后判断哪些是已经验证过两次的,哪些还不算。必会:能用新题组独立完成一次模拟;能把这次的记录和上一次交叉检查、对照。最小示例:上一次模拟赛(S5-05)记录里,T1 用了 25 分钟且写了自测表、T2 的边界数据没测完。这一次换了三道全新的题 T4(简单)、T5(中等)、T6(难题),总时长依旧 120 分钟。做完后对照发现:T4 依旧先排了提交顺序、也写了自测表(用时 22 分钟,比上次略快),这个习惯连续两次都做到了,可以记为稳定;但 T5 的边界数据又一次没顾上测,说明“测边界”这个习惯还没有稳定,需要再验证一次。边界是不能只看这次分数就下结论,也不能只做到一次就宣布习惯已经养成。建议掌握:把两次的时间分配并排列出来比较,具体看哪道题真的变快了。拓展:如果对照后发现某个习惯这次没有做到,要如实标注“还没稳定”,安排下次再验证,而不是凑合算作已经养成或者直接放弃练习。这里不会在浏览器里运行 C++。
diff --git a/lessons/s5-08/index.html b/lessons/s5-08/index.html index 41c842a..49b3834 100644 --- a/lessons/s5-08/index.html +++ b/lessons/s5-08/index.html @@ -991,6 +991,58 @@ background: rgba(24, 55, 47, 0.1); } + .prereq-note { + margin-top: 18px; + padding: 14px 16px; + border: 2px dashed rgba(24, 55, 47, 0.28); + border-radius: 6px; + background: rgba(255, 255, 255, 0.62); + } + + .prereq-note > strong { + display: block; + color: var(--forest); + font-family: "Songti SC", "STSong", serif; + font-size: 1rem; + } + + .prereq-list { + margin: 8px 0 0; + padding-left: 18px; + font-size: 0.78rem; + line-height: 1.65; + } + + .prereq-list li + li { + margin-top: 5px; + } + + .prereq-list span { + display: block; + color: rgba(24, 55, 47, 0.78); + } + + .prereq-link { + color: var(--forest); + font-weight: 800; + text-decoration: underline; + text-decoration-thickness: 2px; + text-underline-offset: 2px; + } + + .prereq-link:hover { + color: var(--orange); + } + + .prereq-path, + .prereq-empty { + margin: 9px 0 0; + color: rgba(24, 55, 47, 0.82); + font-size: 0.76rem; + line-height: 1.6; + } + + .type-grid { display: grid; gap: 12px; @@ -1350,6 +1402,14 @@这一课只练一件事:把五个阶段学过的内容整理成一张,标出还不熟练的并安排一次有针对性的复习,再完成一张,最后看清 CSP-J 和更高级内容之间的边界在哪里。必会:能完成个人知识地图;能标出自己的薄弱模块;能区分 CSP-J 核心与后续拓展。最小示例:把 S1~S5 每个阶段挑 1-2 个知识点列成表格,在“熟练/一般/薄弱”里选一个,比如“递归”标记为“一般”、“DP 状态设计”标记为“薄弱”。给薄弱模块各安排一次具体复习(比如“周三晚上重做 S3-08 的三道 DP 题”),再对照赛前清单确认编译环境、常用代码片段、时间分配计划都准备好了。边界是不能只写“基础不好”这种笼统标注,也不能把 CSP-S 的更高级算法当成 CSP-J 现在就要会。建议掌握:把薄弱模块按优先级排序,先复习出现次数最多、影响面最大的那个。拓展:知道 CSP-S 大致会遇到更复杂的数据结构和算法模型,但现在只需要知道边界在哪,不必现在展开学习。这里不会在浏览器里运行 C++。
diff --git a/package.json b/package.json index db9095d..8e8233e 100644 --- a/package.json +++ b/package.json @@ -24,6 +24,7 @@ "e2e:flow:s3": "CSP_E2E_LESSONS=s3-01,s3-02,s3-03,s3-04,s3-05,s3-06,s3-07,s3-08 bun tests/e2e/flow-cdp.ts", "e2e:flow:s4": "CSP_E2E_LESSONS=s4-01,s4-02,s4-03,s4-04,s4-05,s4-06,s4-07,s4-08 bun tests/e2e/flow-cdp.ts", "e2e:flow:s5": "CSP_E2E_LESSONS=s5-01,s5-02,s5-03,s5-04,s5-05,s5-06,s5-07,s5-08 bun tests/e2e/flow-cdp.ts", - "e2e:keyboard": "bun tests/e2e/keyboard-cdp.ts" + "e2e:keyboard": "bun tests/e2e/keyboard-cdp.ts", + "e2e:prereq": "bun tests/e2e/prereq-cdp.ts" } } diff --git a/scripts/curriculum.ts b/scripts/curriculum.ts index f6e4b5c..043e66e 100644 --- a/scripts/curriculum.ts +++ b/scripts/curriculum.ts @@ -1,6 +1,8 @@ export type Course = { id: string; phase: number; + // 先修课 ID(只写真正会用到的更早的课,按课程顺序排列;第一课为空数组)。 + prerequisites: string[]; title: string; duration: string; objectives: string[]; diff --git a/tests/e2e-count-facts.test.ts b/tests/e2e-count-facts.test.ts index fbfef02..302e0b7 100644 --- a/tests/e2e-count-facts.test.ts +++ b/tests/e2e-count-facts.test.ts @@ -436,10 +436,52 @@ test("键盘清单的固定项编号与脚本声明的每课项数对齐,且 }); test("三个复验脚本都自带项数自查,声明与实跑不符会当场失败", () => { - for (const document of [typedScript, singleLessonScript, keyboardScript]) { + for (const document of [typedScript, singleLessonScript, keyboardScript, prereqScript]) { expect({ document, selfCheck: read(document).includes("检查项数与声明不符") }).toEqual({ document, selfCheck: true, }); } }); + +// ── 先修关系可见与可达段 ──────────────────────────────────────────────────── +// 先修块是每课进页面第一眼看到的东西:位置、文字、链接可达、键盘可达、手机竖屏各有一项检查, +// 再加两项「检查器自己有没有空转」的自检。清单与文档里的项数绑回脚本声明,脚本加了检查而文档 +// 没跟上就会当场失败——本轮就是靠这个把「每课 5 项」改回「每课 6 项」的。 +const prereqScript = "tests/e2e/prereq-cdp.ts"; +const prereqChecklist = "tests/e2e/prereq-manual-checklist.md"; + +test("先修复验的每课项数与总项数都写进了文档", async () => { + const { loadCurriculum } = await import("../scripts/curriculum.ts"); + const lessonCount = (await loadCurriculum()).length; + const perLesson = declaration(prereqScript, "checksPerLesson"); + const selfChecks = declaration(prereqScript, "selfChecks"); + const total = lessonCount * perLesson + selfChecks; + + for (const document of [...proseDocuments, prereqChecklist]) { + const claims = claimsAfter(document, "e2e:prereq"); + + expect({ document, claimed: claims.totals.length > 0 }).toEqual({ document, claimed: true }); + for (const claim of claims.totals) expect({ document, claim }).toEqual({ document, claim: total }); + } + + const row = read(prereqChecklist) + .split("\n") + .filter((line) => line.startsWith("|") && line.includes("prereq-cdp.ts")) + .at(-1); + if (!row) throw new Error(`${prereqChecklist} 里找不到先修复验记录`); + expect(row).toContain(`${total}/${total} 项检查通过`); + expect(row).toContain(`各 ${perLesson} 项`); +}); + +test("先修清单的固定项编号与脚本声明的每课项数对齐,且没有重号", () => { + const source = read(prereqChecklist); + const perLesson = declaration(prereqScript, "checksPerLesson"); + const heading = source.match(/## 每课固定复验的\s*(\d+)\s*项/); + if (!heading) throw new Error(`${prereqChecklist} 里找不到「每课固定复验的 N 项」标题`); + expect(Number(heading[1])).toBe(perLesson); + + const section = source.slice(heading.index! + heading[0].length).split(/\n##\s/)[0]; + const numbers = [...section.matchAll(/^\s*(\d+)\.\s/gm)].map((entry) => Number(entry[1])); + expect(numbers).toEqual(Array.from({ length: perLesson }, (_, index) => index + 1)); +}); diff --git a/tests/e2e/manual-checklist.md b/tests/e2e/manual-checklist.md index 1e1728d..3c44ddd 100644 --- a/tests/e2e/manual-checklist.md +++ b/tests/e2e/manual-checklist.md @@ -22,6 +22,11 @@ (结构检查每课 8 项 + S1-02 与 S3-01 的键盘深路径每课 4 项);只想跑一课或换两课走深路径分别用 `CSP_E2E_KEYBOARD_LESSONS` 与 `CSP_E2E_KEYBOARD_DEEP` 指定,跑的命令本身不变。 +先修关系可见与可达(先修块的位置、文字、链接可达、键盘可达与手机竖屏)另有清单: +[`prereq-manual-checklist.md`](./prereq-manual-checklist.md)。`bun run e2e:prereq` 覆盖全部 40 课, +共 242 项检查(40 课各 6 项:5 项笔记本视口检查 + 1 项手机竖屏检查;另含脚本自检 2 项); +只想跑几课用 `CSP_E2E_PREREQ_LESSONS` 指定,跑的命令本身不变。 + 其余课程暂无自动化脚本,仍按下面的手工步骤执行并在报告或本节记录浏览器与日期。 ## 前置条件 @@ -46,10 +51,12 @@ bun run dev ## E2E-02 课件详情阅读 1. 选择任意一节课,例如 `S4-04 搜索与剪枝`。 -2. 阅读详情中的“学习目标”“具体内容”“检验清单”“视频组织建议”“练习建议”。 -3. 将筛选器切换为“只看未完成”,再切回“全部课件”。 +2. 阅读详情中的“先修”“学习目标”“具体内容”“检验清单”“视频组织建议”“练习建议”。 +3. 看“先修”那一栏:每条都写明课号、课名和那一课要会的一件事,点进去是同一节课。 +4. 选中 `S1-01`,确认“先修”写的是全课程第一课、不用先修,而且没有把课号硬凑成一条链接。 +5. 将筛选器切换为“只看未完成”,再切回“全部课件”。 -**预期:** 每个栏目均有可读内容;筛选改变列表而不清空已选课的内容,并显示筛选结果提示;所有状态还有文字标识,不能只靠颜色表达。 +**预期:** 每个栏目均有可读内容;“先修”排在“学习目标”之前,内容与 `index.html` 的 `courseData` 一致,链接指向真实存在的课目录;筛选改变列表而不清空已选课的内容,并显示筛选结果提示;所有状态还有文字标识,不能只靠颜色表达。 ## E2E-03 进度保存与恢复 diff --git a/tests/e2e/prereq-cdp.ts b/tests/e2e/prereq-cdp.ts new file mode 100644 index 0000000..090f354 --- /dev/null +++ b/tests/e2e/prereq-cdp.ts @@ -0,0 +1,596 @@ +#!/usr/bin/env bun +/** + * 先修关系的浏览器复验(零依赖)。 + * + * 用法: + * bun run e2e:prereq 全部四十课:40 课 × 6 项 + 2 项自检 = 242 项 + * CSP_E2E_PREREQ_LESSONS=s1-01,s3-04 bun tests/e2e/prereq-cdp.ts + * + * 与 `prerequisite-path.test.ts` 的分工:那个文件在 `bun test` 里守住静态契约(先修表合法、 + * 页面文字与 `courseData` 一致、块贴着标题且在开始按钮之前、不靠脚本生成);这个脚本在真实 + * 浏览器里验「学习者真的能用到它」——先修块一进页面就整块落在首屏内(笔记本 1280x1000 与 + * 手机竖屏 390x844 两种视口)、每条链接真的能打开对应的那一课、只用键盘也走得到并且看得见 + * 焦点环、鼠标点下去真的进得去。 + * + * 它不在 `bun test` 范围内:需要真实浏览器,属于体验验收,不是单元测试。 + * 手工清单与复验记录见 tests/e2e/prereq-manual-checklist.md。 + * + * 可用环境变量覆盖默认值: + * CSP_E2E_PORT / CSP_E2E_CDP_PORT / CSP_E2E_CHROME / CSP_E2E_ORIGIN + * CSP_E2E_PREREQ_LESSONS 只跑指定课程,逗号分隔(手机视口那一项跟着一起收窄) + */ + +import { existsSync, mkdtempSync, rmSync } from "node:fs"; +import { tmpdir } from "node:os"; +import { join } from "node:path"; +import { lessonDirectoryName, loadCurriculum } from "../../scripts/curriculum.ts"; + +const projectRoot = `${import.meta.dir}/../..`; +const port = Number(Bun.env.CSP_E2E_PORT ?? 4575); +const cdpPort = Number(Bun.env.CSP_E2E_CDP_PORT ?? 9375); +const origin = Bun.env.CSP_E2E_ORIGIN ?? `http://localhost:${port}`; +const chromeCandidates = [ + Bun.env.CSP_E2E_CHROME, + "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome", + "/Applications/Chromium.app/Contents/MacOS/Chromium", + "/usr/bin/google-chrome", + "/usr/bin/chromium", +].filter((value): value is string => typeof value === "string" && value.length > 0); + +/** 结构检查每课固定 6 项(笔记本视口 5 项 + 手机竖屏 1 项);跑完拿它自查,声明与实跑不符就直接失败。 */ +const checksPerLesson = 6; +/** 页首那两项自检:插一条死链必须被抓到,以及检查后页面必须回到仓库里的样子。 */ +const selfChecks = 2; +/** 键盘检查最多按多少次 Tab;先修块就在页首,前面只有顶栏控件和正文里的 FAQ 按钮,走不到就说明 Tab 顺序被改了。 */ +const maxTabsToPrereq = 24; + +const curriculum = await loadCurriculum(); +const order = curriculum.map((course) => course.id); +const byId = new Map(curriculum.map((course) => [course.id, course])); + +const splitList = (value: string | undefined) => + (value ?? "") + .split(",") + .map((entry) => entry.trim()) + .filter((entry) => entry.length > 0); + +const requested = splitList(Bun.env.CSP_E2E_PREREQ_LESSONS); +for (const directory of requested) { + if (!order.map((id) => lessonDirectoryName(id)).includes(directory)) { + console.error(`不认识这节课:${directory}`); + process.exit(1); + } +} + +const allDirectories = order.map((id) => lessonDirectoryName(id)); +const selectedLessons = requested.length > 0 ? requested : allDirectories; + +type Check = { name: string; ok: boolean; detail: string }; +const checks: Check[] = []; +const servers: Bun.Subprocess[] = []; +const browsers: Bun.Subprocess[] = []; +const tempDirs: string[] = []; + +function check(name: string, ok: boolean, detail = "") { + checks.push({ name, ok, detail }); + console.log(` ${ok ? "ok " : "FAIL"} ${name}${detail ? ` — ${detail}` : ""}`); +} + +function section(title: string) { + console.log(`\n=== ${title} ===`); +} + +async function reachable(url: string) { + try { + const response = await fetch(url, { signal: AbortSignal.timeout(1000) }); + return response.ok; + } catch { + return false; + } +} + +async function waitFor(url: string, description: string, attempts = 120) { + for (let attempt = 0; attempt < attempts; attempt += 1) { + if (await reachable(url)) return; + await Bun.sleep(250); + } + throw new Error(`等待超时:${description}`); +} + +function collect(stream: ReadableStream