夜雨聆风学习资料网

ARTICLE · 1093121

2024 CSP-J 复赛真题及答案解析(完整版)|四题全解

2024 CSP-J 复赛真题及答案解析(完整版)|四题全解

2024 CSP-J 复赛真题及答案解析(完整版)|四题全解

获取 2024CSP-J复赛真题及解析.pdf

请关注状元编程公众号,回复  20260928csp-j

本文分两部分:前半部分为 2024 CSP-J 第二轮(复赛)完整真题,后半部分为四道题的详细解析与满分 C++ 代码。2024 年复赛 T1/T2 送分,T3 贪心构造是区分点,T4 接龙 DP 是压轴难题。建议先通读真题、独立尝试,再对照解析查漏补缺。


第一部分:真题原文

考试说明

  • 考试:CSP-J 2024 第二轮认证(入门级)
  • 时间:2024 年 10 月 26 日
  • 四道题:扑克牌(poker)、地图探险(explore)、小木棍(stick)、接龙(chain)

T1 扑克牌(poker)

【题目描述】

小 P 从同学小 Q 那儿借来一副 n 张牌的扑克牌。本题中不考虑大小王,此时每张牌具有两个属性:花色和点数。花色共有 4 种:方片、草花、红桃和黑桃。点数共有 13 种,从小到大分别为 A23456789TJQK。注意:点数 10 在本题中记为 T。

一副扑克牌是完整的,当且仅当对于每一种花色和每一种点数,都恰好有一张牌具有对应的花色和点数。由此,一副完整的扑克牌恰好有 4×13=52 张牌。

小 P 借来的牌可能不是完整的,为此小 P 准备再向同学小 S 借若干张牌(小 S 每种牌都有无限张)。小 P 想知道他至少得向小 S 借多少张牌,才能让从小 S 和小 Q 借来的牌中,可以选出 52 张牌构成一副完整的扑克牌。

使用字符 D 代表方片,C 代表草花,H 代表红桃,S 代表黑桃,每张牌通过一个长度为 2 的字符串表示,第一个字符表示花色,第二个字符表示点数,例如 CA 表示草花 A,ST 表示黑桃 T。

【输入格式】 输入的第一行包含一个整数 n 表示牌数。接下来 n 行,每行包含一个长度为 2 的字符串描述一张牌。

【输出格式】 输出一行一个整数,表示最少还需要向小 S 借几张牌才能凑成一副完整的扑克牌。

【样例 1】

输入:1SA输出:51

【样例 2】

输入:4DQH3DQDT输出:49

【数据范围】 对于所有测试数据,保证:1 ≤ n ≤ 52,输入的 n 个字符串每个都代表一张合法的扑克牌。


T2 地图探险(explore)

【题目描述】

小 A 派遣机器人去丛林探险。丛林的地图可以用一个 n 行 m 列的字符表来表示。位置 (i,j)(1 ≤ i ≤ n,1 ≤ j ≤ m)的字符为 x 代表有障碍不可通过;为 . 代表空地可以通过。

机器人的状态由位置和朝向两部分组成。朝向用整数 d 表示:d=0 向东,d=1 向南,d=2 向西,d=3 向北。初始时机器人位置为 (x₀, y₀),朝向为 d₀(保证初始位置为空地)。接下来进行 k 次操作,每一步:

  1. 假设当前位置为 (x,y),朝向 d,则下一步位置 (x′,y′):d=0 时 (x,y+1),d=1 时 (x+1,y),d=2 时 (x,y−1),d=3 时 (x−1,y)。
  2. 判断 (x′,y′) 是否在地图内且为空地。若成立则向前走一步(位置变为 (x′,y′),朝向不变);否则执行"向右转"操作,令 d′=(d+1) mod 4,位置保持不变,朝向变为 d′。

小 A 想知道,在机器人执行完 k 步操作之后,地图上所有被机器人经过的位置(包括起始位置)有几个。

【输入格式】 本题有多组测试数据。输入的第一行包含一个正整数 T,表示数据组数。每组数据:第一行三个正整数 n, m, k;第二行两个正整数 x₀, y₀ 和一个非负整数 d₀;接下来 n 行,每行一个长度为 m 的字符串(只包含 x 和 .)。

【输出格式】 对于每组数据,输出一行包含一个正整数,表示被机器人经过的位置个数。

【样例】

输入:21 5 41 1 2....x5 5 201 1 0......xxx..x.x...xx.x....输出:313

【数据范围】 对于所有测试数据,保证:1 ≤ T ≤ 5,1 ≤ n,m ≤ 10³,1 ≤ k ≤ 10⁶,1 ≤ x₀ ≤ n,1 ≤ y₀ ≤ m,0 ≤ d₀ ≤ 3,且机器人的起始位置为空地。


T3 小木棍(stick)

【题目描述】

小 S 喜欢收集小木棍。在收集了 n 根长度相等的小木棍之后,他闲来无事,便用它们拼起了数字。用小木棍拼每种数字的方法:数字 0、6、9 各需 6 根,1 需 2 根,2、3、5 各需 5 根,4 需 4 根,7 需 3 根,8 需 7 根。

小 S 希望拼出一个正整数,满足如下条件:

  • 拼出这个数恰好使用 n 根小木棍;
  • 拼出的数没有前导 0;
  • 在满足以上两个条件的前提下,这个数尽可能小。

如果不存在正整数满足以上条件,输出 −1 进行报告。

【输入格式】 本题有多组测试数据。输入的第一行包含一个正整数 T,表示数据组数。每组数据一行包含一个整数 n,表示木棍数。

【输出格式】 对于每组数据,输出一行,如果存在满足题意的正整数,输出这个数;否则输出 −1。

【样例】

输入:5123618输出:-1176208

【数据范围】 对于所有测试数据,保证:1 ≤ T ≤ 50,1 ≤ n ≤ 10⁵。


T4 接龙(chain)

【题目描述】

总共有 n 个人参与接龙游戏,第 i 个人会获得一个整数序列 Sᵢ 作为他的词库。一次游戏分为若干轮,每一轮规则如下:

  • n 个人中的某个人 p 带着他的词库 Sₚ 进行接龙。若这不是游戏的第一轮,那么这一轮进行接龙的人不能与上一轮相同,但可以与上上轮或更往前的轮相同。
  • 接龙的人选择一个长度在 [2,k] 的 Sₚ 的连续子序列 A 作为这一轮的接龙序列,其中 k 是给定的常数。若这是游戏的第一轮,那么 A 需要以元素 1 开头,否则 A 需要以上一轮的接龙序列的最后一个元素开头。

小 J 给了 n 个参与游戏的人 q 个任务,第 j 个任务需要这 n 个人进行一次游戏,在这次游戏里进行恰好 rⱼ 轮接龙,且最后一轮的接龙序列的最后一个元素恰好为 cⱼ。请判断这 q 个任务是否可以完成。

【输入格式】 本题有多组测试数据。输入的第一行包含一个正整数 T,表示数据组数。每组数据:第一行三个整数 n, k, q;接下来 n 行,第 i 行包含 (lᵢ+1) 个整数 lᵢ, Sᵢ,₁, …, Sᵢ,ₗᵢ;接下来 q 行,第 j 行两个整数 rⱼ, cⱼ。

【输出格式】 对于每个任务,输出一行包含一个整数,若任务可以完成输出 1,否则输出 0。

【样例】

输入:13 3 75 1 2 3 4 13 1 2 53 5 1 61 21 42 43 46 61 17 7输出:1010100

【数据范围】 对于所有测试数据,保证:1 ≤ T ≤ 5;1 ≤ n ≤ 10⁵,2 ≤ k ≤ 2×10⁵,1 ≤ q ≤ 10⁵;1 ≤ lᵢ ≤ 2×10⁵,1 ≤ Sᵢ,ⱼ ≤ 2×10⁵;1 ≤ rⱼ ≤ 10²,1 ≤ cⱼ ≤ 2×10⁵;单组测试数据内所有 lᵢ 的和 ∑l ≤ 2×10⁵。


第二部分:题目解析

T1 扑克牌(poker)|集合去重

  • 考点:去重 / 集合(set)/ 模拟
  • 难度:入门,送分题

思路:已有 n 张牌(可能重复),问至少再借几张才能凑齐 52 张完整扑克牌(4 花色 × 13 点数)。用 set<string> 对输入牌去重,统计不同牌的数量 cnt,答案 = 52 − cnt。

易错点:① 重复的牌只算一张;② 点数 10 记作 T,按字符串处理最稳。

#include<bits/stdc++.h>usingnamespace std;intmain(){int n;    cin >> n;    set<string> st;for (int i = 0; i < n; i++) {        string s;        cin >> s;        st.insert(s);    }    cout << 52 - (int)st.size() << '\n';   // 不同牌的数量return0;}

T2 地图探险(explore)|方向模拟

  • 考点:模拟 / 方向数组 / 判重
  • 难度:普及−,送分模拟题

思路:机器人按朝向每步前进,遇障碍或越界则右转(不移动),问 k 步内经过的不同格子数(含起点)。方向数组 d=0 东、1 南、2 西、3 北,即 (0,1)、(1,0)、(0,−1)、(−1,0);右转 d=(d+1)%4。用二维 bool 数组记录访问,访问新格时计数。

易错点:① 每步至多右转一次(不移动),不要循环转向;② 起点也算经过;③ 多组数据要重置访问数组。

#include<bits/stdc++.h>usingnamespace std;constint MAXN = 1005;char mp[MAXN][MAXN];bool vis[MAXN][MAXN];int dx[4] = {0, 1, 0, -1};   // 东、南、西、北int dy[4] = {1, 0, -1, 0};intmain(){    ios::sync_with_stdio(false);    cin.tie(nullptr);int T; cin >> T;while (T--) {int n, m, k;        cin >> n >> m >> k;int x, y, d;        cin >> x >> y >> d;for (int i = 1; i <= n; i++)for (int j = 1; j <= m; j++) { cin >> mp[i][j]; vis[i][j] = false; }        vis[x][y] = true;int cnt = 1;for (int step = 0; step < k; step++) {int nx = x + dx[d], ny = y + dy[d];if (nx >= 1 && nx <= n && ny >= 1 && ny <= m && mp[nx][ny] == '.') {                x = nx; y = ny;if (!vis[x][y]) { vis[x][y] = true; cnt++; }            } else {                d = (d + 1) % 4;   // 右转一次,不移动            }        }        cout << cnt << '\n';    }return0;}

T3 小木棍(stick)|贪心构造

  • 考点:贪心 / 可行性构造
  • 难度:普及,2024 年的区分题

思路:用恰好 n 根火柴拼出无前导 0 的最小正整数(各数字所需火柴数:0,6,9→6;1→2;2,3,5→5;4→4;7→3;8→7)。

关键贪心:位数越少数越小 → 最少位数 = ⌈n/7⌉(每位数最多用 7 根,如 8)。逐位构造:从高位到低位,每次选"当前可行的最小数字"——首位用 1..9、其余位用 0..9,需保证剩余木棍数能恰好填满剩余位数(每位 2~7 根)。n=1 无解输出 −1。

易错点:① 必须恰好用完 n 根(不是不超过);② 首位不能为 0;③ "位数最少"优先于"首位最小"。

#include<bits/stdc++.h>usingnamespace std;int cost[10] = {6, 2, 5, 5, 4, 5, 6, 3, 7, 6};   // 0..9 各自所需木棍数intmain(){    ios::sync_with_stdio(false);    cin.tie(nullptr);int T; cin >> T;while (T--) {int n; cin >> n;if (n == 1) { cout << -1 << '\n'; continue; }int digits = (n + 6) / 7;      // 最少位数:每位最多用 7 根(数字 8)        string ans;int rem = n;for (int pos = 0; pos < digits; pos++) {int left = digits - pos - 1;   // 剩余位数for (int dig = (pos == 0 ? 1 : 0); dig <= 9; dig++) {int c = cost[dig];int rest = rem - c;if (pos == digits - 1) {if (rest == 0) { ans += char('0' + dig); rem = 0; break; }                } elseif (rest >= 2 * left && rest <= 7 * left) {                    ans += char('0' + dig); rem = rest; break;                }            }        }        cout << ans << '\n';    }return0;}

T4 接龙(chain)|轮次 DP + 排除计数

  • 考点:动态规划 / 计数排除 / 区间查询优化
  • 难度:提高−,六年 T4 中最考验实现功底的一道

思路:

设 g_r[x] = 第 r 轮能以元素 x 结尾的玩家数(同一玩家多种方案只计 1)。

  • 对第 r 轮、玩家 p:位置 i 能作为"上一轮末元素"当且仅当 g_{r-1}[Sᵢ] − [p 上轮能以 Sᵢ 结尾] > 0(排除 p 自己接龙自己,即相邻两轮不能同一人)。
  • 玩家 p 能以位置 j 的元素结尾 ⟺ 存在 i ∈ [j−k+1, j−1] 使上述条件成立 → 用前缀和/滑动窗口 O(1) 判断。
  • 每轮扫一遍所有玩家的序列(总量 ∑l ≤ 2×10⁵),r ≤ 100 → 总 O(r·∑l)。

易错点:① 相邻轮次不能同一玩家 → 计数时要减去玩家自己上轮的贡献;② 子序列长度 ∈[2,k],起点 i 最远到 j−1;③ 第一轮任意玩家均可,且必须从元素 1 开头。

#include<bits/stdc++.h>usingnamespace std;constint MAXV = 200005;intmain(){    ios::sync_with_stdio(false);    cin.tie(nullptr);int T; cin >> T;while (T--) {int n, k, q;        cin >> n >> k >> q;        vector<vector<int>> S(n + 1);int maxR = 0;for (int i = 1; i <= n; i++) {int l; cin >> l;            S[i].resize(l);for (int j = 0; j < l; j++) cin >> S[i][j];        }        vector<pair<int,int>> queries(q);for (int i = 0; i < q; i++) {            cin >> queries[i].first >> queries[i].second;            maxR = max(maxR, queries[i].first);        }// g[x]: 上一轮能以元素 x 结尾的玩家数vector<int> g(MAXV, 0);        vector<vector<int>> prevSet(n + 1), curSet(n + 1);// 第 1 轮:任意玩家,子序列以 1 开头,长度 ∈ [2, k]for (int p = 1; p <= n; p++) {int len = (int)S[p].size();for (int i = 0; i < len; i++) {if (S[p][i] != 1) continue;for (int j = i + 1; j < len && j - i + 1 <= k; j++)                    curSet[p].push_back(S[p][j]);            }sort(curSet[p].begin(), curSet[p].end());            curSet[p].erase(unique(curSet[p].begin(), curSet[p].end()), curSet[p].end());for (int x : curSet[p]) g[x]++;        }        prevSet.swap(curSet);// 逐轮转移for (int r = 2; r <= maxR; r++) {vector<int> ng(MAXV, 0);for (int p = 1; p <= n; p++) {auto canPrev = [&](int x) {auto &v = prevSet[p];returnbinary_search(v.begin(), v.end(), x);                };const vector<int> &seq = S[p];int len = (int)seq.size();vector<int> pref(len + 1, 0);for (int i = 0; i < len; i++) {int avail = g[seq[i]] - (canPrev(seq[i]) ? 1 : 0);                    pref[i + 1] = pref[i] + (avail > 0 ? 1 : 0);                }                vector<int> &cur = curSet[p];                cur.clear();for (int j = 0; j < len; j++) {int lo = max(0, j - k + 1), hi = j - 1;if (lo <= hi && pref[hi + 1] - pref[lo] > 0)                        cur.push_back(seq[j]);                }sort(cur.begin(), cur.end());                cur.erase(unique(cur.begin(), cur.end()), cur.end());for (int x : cur) ng[x]++;            }            g.swap(ng);            prevSet.swap(curSet);        }for (auto &qr : queries)            cout << (g[qr.second] > 0 ? 1 : 0) << '\n';    }return0;}

四题考点总结

题号
题目
核心算法
难度
T1
扑克牌
集合去重
入门送分
T2
地图探险
方向模拟 + 判重
普及−送分
T3
小木棍
贪心 + 可行性构造
普及区分
T4
接龙
轮次 DP + 排除计数
提高−压轴

💡 年份点评:2024 年 T1/T2 送分,T3 贪心构造(中等),T4 接龙 DP 是绝对的区分题。知识结构是"集合去重 + 方向模拟 + 贪心构造 + 轮次 DP 与排除计数"。"恰好用完"的可行性判断、滑动窗口把区间条件化成前缀和、以及"排除同一玩家"的计数技巧,是今年最重要的三个方法。


本文真题基于 2024 CSP-J 第二轮认证官方试卷整理,答案与解析供学习参考,最终以 CCF 官方发布为准。祝各位同学复赛顺利!

相关学习资料