页面受保护:禁止保存 / 下载 / 打印,文字可正常复制
可达信奥 · 学情诊断
2026 合肥市瑶海区小学生信息学竞赛 · 试卷复盘

朱旭扬 同学
这卷最可惜的不是不会做。

五题中,第 1 题算法完全正确,却因一句 C++11 新语法在旧编译器下编译失败(CE),100 分整题归零。其余失分集中在边界遗漏、栈结构没存住、以及第 4 题空题。

学校琥珀名城小学教育集团明皇路小学
年级五年级
座位号826088
随机码4186
总分95 / 500
当前总分
95/ 500
五题得分累加
编译事故失分
100分
第 1 题 CE 白丢
可立即找回
~180分
改语法 + 修 rise/board
算法底子
良好
supply 思路正确
CE 事故

第 1 题「补充彩笔」:算法满分,编译 0 分

题目求 \(\sum_{i=1}^{n}\max(0,\; k - a_i)\),旭扬写的逻辑完全正确,样例 10、边界都过。但他用了五份代码里唯一一处 C++11 写法 for(auto it : vc),在旧标准编译器下直接编译失败,判题端一个点都跑不出来 → 0 分。

01

五题得分全景

条形长度 = 实际得分 / 满分 100

各题得分对比(红 = 事故 / 空题,琥珀 = 部分正确)

100 分失分归因

学情画像(五维能力雷达)

★

旭扬的亮点(先肯定,再谈问题)

读懂题了,思路对

supply 是五题里最简单的,但他准确抓住了"每盒补到 k 支"就是 Σ max(0,k−aᵢ),算法逻辑全对。这说明读题能力和最基础的求和建模已经过关。

方向感在,只是手生

board 他写出了 pArr.push_back / pop_back,说明他"知道这是个栈";rise 他枚举起点往后扫,也是经典做法。方向都对,败在细节没踩稳。

样例意识强

batch 的两个样例他都跑对了(23 和 11),说明他会拿样例自测、会输出结果。问题不在"不会验证",而在"样例过了就以为全过"。

02

逐题病因诊断

代码已逐题编译实测
0
满分 100
编译错误 CE
第 1 题 · 补充彩笔 supply
题意:每盒补到至少 k 支,求最少添加总数。
思路还原:他读完题立刻反应过来"哪盒不够就补差量",遍历一遍求和——这是最直接也最正确的建模。代码逻辑我实测过,样例、边界、n=1、全 ≥k 全部通过。
根因(不是算法问题):五份代码里唯独这题写了 for(auto it : vc) 范围 for 循环——这是 C++11 才有的语法。其余四题全是老式下标循环,所以唯独这题在旧编译器下编译失败。这是"工程规范"丢分,不是"不会做"丢分。
$ g++ -std=c++98 supply.cpp
supply.cpp:15:18: error: 'it' does not name a type
rise / board / batch.cpp  →  编译通过
AC 参考代码(改一行即可满分)
#include <bits/stdc++.h>
using namespace std;
int main(){
    ifstream fin("supply.in");
    ofstream fout("supply.out");
    int n, k; fin >> n >> k;
    long long sum = 0;
    for (int i = 0; i < n; i++) {
        int x; fin >> x;
        if (x < k) sum += k - x;
    }
    fout << sum;
    return 0;
}
教学点:考前第一步先确认判题环境支持的 C++ 标准版本(CSP-J/S 现普遍支持 C++14/17,但区赛、校内赛、部分培训机构的旧 OJ 可能仍用 g++ 默认的 gnu++98)。环境确认前保守写法:不写 range-for、auto、lambda、unordered_map;环境确认后再放心用现代语法。本地编译通过 ≠ 判题机编译通过,交卷前心里要跑一遍"判题机的编译器"。
知识点:模拟求和易错:CE / 语法规范同类题:补足够 / 凑钱
20
满分 100
边界遗漏
第 2 题 · 卡片排列 rise
题意:最长连续严格递增子段长度。
思路还原:他用双重循环枚举每个起点 i,从 i+1 往后扫,遇到不满足就 break。这个思路本身对,样例 1(答案 3)也跑对了。
根因:他只在内层 break 那一刻才更新 maxCnt。如果最长递增段正好从某个位置一直延伸到数组末尾,内层循环自然结束、不触发 break,答案就漏更新了。
实测输入 1 2 3 4 5(答案 5)他输出 1——正是这个 bug。另外他 O(n²) 的写法在 n=200000 时还会 TLE,就算修好 bug 也过不了大数据点。
AC 参考代码(O(n) 一遍扫描,末尾必更新)
#include <bits/stdc++.h>
using namespace std;
int a[200005];
int main(){
    ifstream fin("rise.in");
    ofstream fout("rise.out");
    int n; fin >> n;
    for (int i = 0; i < n; i++) fin >> a[i];
    int ans = 1, cnt = 1;
    for (int i = 1; i < n; i++) {
        if (a[i] > a[i-1]) cnt++;
        else cnt = 1;
        ans = max(ans, cnt);   // 每一步都更新,不依赖 break
    }
    fout << ans;
    return 0;
}
教学点:"在线扫描"类题目的铁律——答案更新放在循环体里,不要放在 break / continue 分支里。否则一旦循环正常走完(没有触发跳出),答案就丢了。养成习惯:每处理一个元素立刻更新答案,不要等"出了问题再回头算"。
知识点:线性扫描 / 双指针易错:末尾漏更新复杂度:O(n)
50
满分 100
数据结构用错
第 3 题 · 展板回退 board
题意:涂色压栈、擦除弹栈,本质是栈。
思路还原:他写出了 pArr.push_back(p) 和 pArr.pop_back(),说明他清楚"这是个后进先出的栈"。样例 1 能过,证明单次涂色、单次擦除他完全会。
根因:他把栈数组 pArr 和 lastP 写在了 m 次操作的循环体里面——每一轮循环开始时,这两个变量都被重新声明、重新初始化为空,等于上一轮涂的颜色"记忆"全丢了。现在样例能过,只是靠编译器在栈内存里没覆盖掉上一轮的值(UB 行为)。
实测连续两次擦除:涂2→涂4→擦→擦(应全 0),他输出 0 5 0 0——第二次擦除本该擦掉位置 2,结果因为 lastP 还残留着 4,又擦了一次位置 4,位置 2 留了下来。
AC 参考代码(栈必须在循环外声明)
#include <bits/stdc++.h>
using namespace std;
int color[105];
vector<int> stk;          // ★ 循环外声明,整轮操作共用
int main(){
    ifstream fin("board.in");
    ofstream fout("board.out");
    int n, m; fin >> n >> m;
    for (int i = 0; i < m; i++) {
        int op; fin >> op;
        if (op == 1) {
            int p, c; fin >> p >> c;
            color[p] = c;
            stk.push_back(p);
        } else {
            int p = stk.back(); stk.pop_back();
            color[p] = 0;
        }
    }
    for (int i = 1; i <= n; i++) {
        fout << color[i];
        if (i < n) fout << ' ';
    }
    fout << endl;
    return 0;
}
教学点:"需要跨多次循环记住的状态"必须声明在循环外面。栈、队列、计数器、前缀和——这些都是循环外变量。判断口诀:"这个变量的值要不要带到下一轮?"要 → 写循环外;不要 → 写循环内。
知识点:栈 stack易错:变量作用域同类题:括号匹配 / 浏览器后退
0
满分 100
空题未完成
第 4 题 · 物资配送 deliver
题意:网格最短路,每个 T 往返距离排序贪心。
思路还原:他至少开了题——读进了 n、m、B,把地图字符也读进了 Map 数组。说明他读题、建模的第一步做了,但走到 BFS 这一步卡住,没往下写。
根因:这题考两个知识点:① 网格 BFS 求最短路;② 把所有 T 到 S 的往返距离排序,贪心选能在 B 步内送完的最多数量。他两个都没写,连 ofstream 都没开,等于空交。这不是 bug,是知识点没学过 / 考场时间不够。
$ cat deliver.cpp
int main() {
    ifstream fin("deliver.in");
    fin >> n >> m >> B;
    for (...) cin >> Map[i][j];
}   // 函数直接结束:无 BFS、无距离、无 fout
AC 参考代码(BFS + 贪心)
#include <bits/stdc++.h>
using namespace std;
int n, m, B, sx, sy;
char g[505][505];
int d[505][505];
int dx[4]={-1,1,0,0}, dy[4]={0,0,-1,1};
int main(){
    ifstream fin("deliver.in");
    ofstream fout("deliver.out");
    fin >> n >> m >> B;
    vector<int> tx, ty;
    for (int i = 0; i < n; i++) {
        fin >> g[i];
        for (int j = 0; j < m; j++) {
            if (g[i][j] == 'S') {
                sx = i; sy = j;
            } else if (g[i][j] == 'T') {
                tx.push_back(i);
                ty.push_back(j);
            }
        }
    }
    memset(d, -1, sizeof(d));
    queue<pair<int,int> > q;
    d[sx][sy] = 0;
    q.push(make_pair(sx, sy));
    while (!q.empty()) {
        int x = q.front().first;
        int y = q.front().second;
        q.pop();
        for (int k = 0; k < 4; k++) {
            int nx = x + dx[k];
            int ny = y + dy[k];
            if (nx < 0 || nx >= n || ny < 0 || ny >= m) continue;
            if (g[nx][ny] == '#' || d[nx][ny] != -1) continue;
            d[nx][ny] = d[x][y] + 1;
            q.push(make_pair(nx, ny));
        }
    }
    vector<int> dists;
    for (int i = 0; i < (int)tx.size(); i++) {
        int x = tx[i], y = ty[i];
        if (d[x][y] != -1) dists.push_back(2 * d[x][y]);
    }
    sort(dists.begin(), dists.end());
    int used = 0, ans = 0;
    for (int i = 0; i < (int)dists.size(); i++) {
        if (used + dists[i] <= B) {
            used += dists[i];
            ans++;
        } else {
            break;
        }
    }
    fout << ans;
    return 0;
}
教学点:网格题看到"最短 / 最少步"立刻想 BFS;看到"每次往返、选最多"立刻想排序贪心。这两个是 CSP-J 高频组合,必须专项刷到形成条件反射。
知识点:网格 BFS知识点:贪心排序同类题:迷宫最短路 / 加油站进度:可达信奥 L4 核心内容,尚未学到
25
满分 100
题意理解偏差
第 5 题 · 分批装箱 batch
题意:把 n 件物品分成若干连续批次,每批 ≤ k 件,批费用 = 批内最大值,求最小总费用。
思路还原:他把"分批"理解成了"把连续相同的数合并成一段",然后每段取段首求和。两个样例都过了(23 和 11)——因为样例 2 数字全不同、样例 1 中 k=2 时恰好等价。这是典型的"样例驱动编程":样例过了就以为理解对了。
根因:这是一道区间 DP,不是"合并相邻相同元素"。他没有建立"状态 dp[i] = 前 i 件物品的最小费用"这个 DP 思维,而是直接在原数组上做了一个直觉的贪心分组。样例的巧合掩盖了模型错误,所以只在 k=1(每批一件)这种退化情况下碰对了 5 个点。
正确做法(区间 DP 转移方程): $$dp[i]=\min_{\,j\in[i-k,\,i-1]}\Big(dp[j]+\max(a_{j+1..i})\Big)$$
含义:最后一批取 j+1 ~ i,费用是这段的最大值;枚举上一批的结束位置 j,取最小。
AC 参考代码(O(nk) 区间 DP)
#include <bits/stdc++.h>
using namespace std;
int a[10005], dp[10005];
int main(){
    ifstream fin("batch.in");
    ofstream fout("batch.out");
    int n, k; fin >> n >> k;
    for (int i=1;i<=n;i++) fin >> a[i];
    dp[0] = 0;
    for (int i=1;i<=n;i++){
        dp[i] = INT_MAX;
        int mx = 0;
        for (int j=i; j>=max(1,i-k+1); j--){
            mx = max(mx, a[j]);
            dp[i] = min(dp[i], dp[j-1] + mx);
        }
    }
    fout << dp[n];
    return 0;
}
教学点:看到"分成若干段、每段有限制、求最小 / 最大费用"——立刻想 DP,不要在原数组上贪心。判断口诀:"最后一段从哪切?"枚举切点 j,转移就是 dp[j] + 这段的贡献。
知识点:区间 DP易错:样例巧合 ≠ 模型正确复杂度:O(nk)进度:可达信奥 L4 核心内容,尚未学到
03

四周针对性训练计划

按优先级排序,每周一个主题
第 1 周
工程规范专项

目标:下次考试 0 CE、0 编译警告。

  • 考前确认判题机 C++ 标准版本(C++98 / 14 / 17)
  • 未知环境下用保守写法,已知环境再用现代语法
  • 文件 I/O 规范:ifstream/ofstream 路径确认
  • 每次交卷前按判题机标准本地编译一次
题量:5 道简单模拟题,重点不在算法,在"一次编译过"
第 2 周
边界与状态专项

目标:在线扫描类题不再漏末尾 case。

  • 练习"每步更新答案"的扫描写法
  • 区分循环内变量 / 循环外变量
  • 栈、队列的生命周期训练
题量:rise 类最长子段 5 道 + board 类栈操作 5 道
第 3 周
网格 BFS 专项

目标:看到网格最短路径立刻 BFS。

  • 四方向 BFS 模板默写
  • 多源 BFS、障碍 BFS 变式
  • BFS + 排序贪心组合(deliver 类)
题量:迷宫最短路 5 道 + deliver 类 3 道
第 4 周
区间 DP 入门

目标:建立"最后一段从哪切"的 DP 思维。

  • 线性 DP → 区间 DP 过渡
  • "分段取最值"模型专题
  • 破除"样例过 = 模型对"的错觉
题量:简单 DP 5 道 + batch 类分段 DP 3 道
学情备注:第 4 题 deliver(网格 BFS + 贪心)和第 5 题 batch(区间 DP)属于可达信奥 L4 阶段核心课程内容,旭扬目前进度尚未覆盖。这两题的失分属于"知识未学"而非"能力不足"——他能开题、能读样例、能写部分代码,说明基础在,只是知识点还没轮到。第 1、2、3 周的工程规范、边界意识、栈结构才是他当前阶段真正要补的;第 3、4 周训练计划按 L4 课程进度自然推进即可,不必超前加压。