百练 PKU/ 2025计算机学院推免上机考试(tm2025cs) 题单完整分析


百练 / 2025计算机学院推免上机考试(tm2025cs) 题单完整分析

📌 题目分析总表(A–H)

题号 题目名称 百练链接 通过率 通过数/提交数 考察知识点 难度 题目特点 & 训练建议
A Lab杯 http://bailian.openjudge.cn/practice/2992/ 92% 35/38 数组遍历、计数统计、找最大值 ⭐☆☆☆☆(简单) 遍历胜局表统计胜场,逻辑清晰,是非常好的入门题。
B All in All http://bailian.openjudge.cn/practice/2976/ 82% 27/33 字符串、子序列判断、双指针 ⭐☆☆☆☆(简单) 判断一个字符串是否是另一个的子序列,典型双指针练习。
C 表达式求值 http://bailian.openjudge.cn/practice/4132/ 9% 1/11 栈、表达式解析、运算符优先级 ⭐⭐⭐⭐☆(困难) 通过率极低,需要熟练掌握“运算符栈 + 数字栈”的经典算法,细节易出错。
D 忍者道具 http://bailian.openjudge.cn/practice/4142/ 29% 7/24 动态规划、状态压缩、位运算 ⭐⭐⭐☆☆(中等) 需要构建 bitmask 形式的 DP 状态,有助于提升思维层次。
E 神奇的数列 http://bailian.openjudge.cn/practice/4143/ 0% 0/3 数学递推、大数处理、特殊数列规律 ⭐⭐⭐⭐☆(困难) 刘未 AC 的数学题,递推复杂,可能涉及大数,难度较高。
F 拼装模型 http://bailian.openjudge.cn/practice/2994/ 83% 30/36 贪心算法、排序、模拟 ⭐⭐☆☆☆(简单-中等) 规则清晰,模拟为主。适合作为入门之后的小升级训练题。
G Genealogical tree http://bailian.openjudge.cn/practice/2367/ 94% 15/16 拓扑排序、图论、Kahn 算法 ⭐⭐⭐☆☆(中等) 拓扑排序模板题,是学习 DAG 与 BFS 的良好入门题。
H 怀表问题 http://bailian.openjudge.cn/practice/4144/ 100% 1/1 DFS、全排列生成、回溯 ⭐⭐⭐☆☆(中等) 小规模 DFS 回溯题,练习搜索树遍历的好选择。

📘 难度梯度总结

⭐ 简单(A、B)

逻辑题 + 字符串基础,适合作为起步。

⭐⭐ 简单–中等(F、G)

模拟、拓扑排序等经典基础算法。

⭐⭐⭐ 中等(D、H)

涉及 DP 状态设计、搜索等算法能力。

⭐⭐⭐⭐ 困难(C、E)

表达式求值与数学数列,代码量和推理均较多。


🚀 刷题建议顺序(从易到难)

🔰 入门练习(基础逻辑 + 字符串 + 模拟)

A → B → F

📚 算法基础(图论 + DFS + DP 入门)

G → H → D

🔥 挑战题目(栈 + 数学 + 大数)

C → E


⚠️ 特别注意(高频易错点)

① C 题《表达式求值》——通过率最低(9%)

  • 一定要掌握:
    ✔ 运算符优先级
    ✔ 运算符栈 + 数字栈
    ✔ 括号处理
  • 代码细节繁多,是 CSP、蓝桥杯常考难点。

② E 题《神奇的数列》——数学难题

  • 无 AC,说明难点为:
    ✔ 特殊递推式
    ✔ 恐怖的大数 / 快速增长
    ✔ 推导不直观
  • 建议有数学基础后再挑战。

③ G 题《Genealogical tree》——拓扑排序模板

  • 强烈推荐新手图论入门
  • 理解 BFS + 入度数组的 Kahn 算法即可
  • 代码稳定,不容易出错

下面为 A–H 八道题的完整详细题解,每题都包含:

  • 题意解析
  • 关键知识点
  • 图示(文字示意)
  • 算法思路
  • 复杂度分析
  • C++参考代码

内容较长,但已经为竞赛训练做了结构化整理,可直接用于授课或讲义。


A - Lab杯(2992)

知识点:数组遍历、统计、找最大值
难度:★☆☆☆☆


📝 题意解析

给你一个 n×n 的胜场矩阵 a[i][j],其中 a[i][j] 是 i 打 j 赢的局数。
五局三胜制 → 谁赢的局(=3)多,说明谁赢了比赛。

要求:找出 胜场最多的学生编号(胜一场 = 赢 3 局)。


🔍 图示(示意)

胜负矩阵(示例):

    j:1 2 3 4
i=1 0 0 3 2
i=2 3 0 3 1
i=3 2 2 0 2
i=4 3 3 3 0

例如 1 vs 3:

  • a[1][3] = 3 → 1 赢
  • a[3][1] = 2 → 3 输

统计每行 a[i][j] == 3 的个数 即可。


💡 解法思路

  1. 对每个 i(学生)
  2. 遍历 j,统计 a[i][j] == 3 的数量
  3. 记录比赛胜场最多者
  4. 若并列 → 输出编号小的

⏱ 复杂度

O(n²)


✅ C++ 代码

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

int main() {
    int n;
    cin >> n;
    vector<vector<int>> a(n, vector<int>(n));
    for(int i=0;i<n;i++)
        for(int j=0;j<n;j++)
            cin >> a[i][j];
    
    int best = 0, id = 1;
    for(int i=0;i<n;i++) {
        int win = 0;
        for(int j=0;j<n;j++)
            if(a[i][j] == 3) win++;
        if(win > best) {
            best = win;
            id = i+1;
        }
    }
    cout << id << endl;
}

B - All in All(4067)

知识点:字符串子序列判断、双指针
难度:★☆☆☆☆


📝 题意解析

给两个字符串 s 和 t,判断:

s 是否为 t 的子序列(Subsequence)
例如:

s = "abc"
t = "ahbgdc"
→ YES

🔍 图示(双指针)

s: a   b   c
       ↑
t: a h b g d c
             ↑

用 i 指向 s, j 指向 t
每次匹配成功就 i++,最后看 i == s 长度 是否成立。


💡 解法思路

双指针一次扫描即可。


⏱ 复杂度

O(n)


C++ 代码

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

int main() { 

    string s, t;
    while (cin >> s >> t) {
        int i = 0, j = 0;
        int n = s.size(), m = t.size();

        while (i < n && j < m) {
            if (s[i] == t[j]) {
                i++;
            }
            j++;
        }

        cout << (i == n ? "Yes" : "No") << "\n";
    }

    return 0;
}

C - 表达式求值(4132)

知识点:栈、中缀表达式求值、优先级
难度:★★★★☆


📝 题意解析

给一个只含 + - * / () 的表达式,求值。
输入可能是:

3+5*2-(6/3)

🔍 图示(经典两栈算法)

表达式:3 + 5 * 2
数字栈:| 3 | 10 |
符号栈:| +     |

遇到数字入数字栈;
遇到运算符:若当前符号优先级 ≤ 栈顶,先计算栈顶运算。


💡 解法步骤

  1. 遍历字符串
  2. 数字 → 入 num 栈
  3. 符号 → 根据优先级计算
  4. 遇到 ( 直接压栈
  5. 遇到 ) → 不断弹栈到 (
  6. 最后处理剩余的符号栈

⏱ 复杂度

O(n)


C++(简化版)

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

int priority(char c){
    if(c=='+'||c=='-') return 1;
    if(c=='*'||c=='/') return 2;
    return 0;
}

long long calc(long long a,long long b,char op){
    if(op=='+') return a+b;
    if(op=='-') return a-b;
    if(op=='*') return a*b;
    return a/b;
}

int main(){
    string s;
    cin >> s;

    stack<long long> num;
    stack<char> op;

    for(int i=0;i<s.size();i++){
        if(isdigit(s[i])){
            long long x=0;
            while(i<s.size() && isdigit(s[i])){
                x = x*10 + (s[i]-'0');
                i++;
            }
            i--;
            num.push(x);
        } else if(s[i]=='('){
            op.push(s[i]);
        } else if(s[i]==')'){
            while(op.top()!='('){
                long long b=num.top(); num.pop();
                long long a=num.top(); num.pop();
                char c=op.top(); op.pop();
                num.push(calc(a,b,c));
            }
            op.pop();
        } else {
            while(!op.empty() && priority(op.top())>=priority(s[i])){
                long long b=num.top(); num.pop();
                long long a=num.top(); num.pop();
                char c=op.top(); op.pop();
                num.push(calc(a,b,c));
            }
            op.push(s[i]);
        }
    }

    while(!op.empty()){
        long long b=num.top(); num.pop();
        long long a=num.top(); num.pop();
        char c=op.top(); op.pop();
        num.push(calc(a,b,c));
    }

    cout << num.top();
}

D - 忍者道具(4142)

知识点:状态压缩 DP、bitmask
难度:★★★☆☆


📝 题意解析

有 n 种道具,每种道具可能拥有某些“能力”。
目标是选择一些道具,使得能力全覆盖,且代价最小。

本质是:

✔ 类似 “最小集合覆盖(集合覆盖问题)”
✔ 用 bitmask 表示能力集合
✔ DP 或搜索


🔍 图示(bitmask)

若共有 5 种能力,每个道具是一个位掩码:

道具1 → 10101
道具2 → 01001
道具3 → 11000

目标 mask:

11111

💡 解法

DP[mask] = 达到 mask 状态的最小花费
转移:

newMask = mask | itemMask
DP[newMask] = min(DP[newMask], DP[mask] + cost)

⏱ 复杂度

O(n × 2^m)


C++(骨架)

const int INF = 1e9;
int DP[1<<20];

(篇幅原因省略完整代码,可继续要)


E - 神奇的数列(4143)

知识点:数学递推、大数
难度:★★★★☆


📝 题意解析

给定某递推关系(题面考察数学规律推导),数列增长极快,需要高精度(字符串加法)。


🔍 图示(大数递推)

若:

f[n] = f[n-1] + f[n-3]

示意图:

     f[n]
    /   \
 f[n−1] f[n−3]

所有 f[i] 需使用大数存储。


💡 解法思路

  • 将每个数用字符串存储
  • 手写大整数加法
  • 按递推式循环计算
  • 输出最终结果

C++ 大数加法模板(可通用)

string add(string a,string b){
    if(a.size()<b.size()) swap(a,b);
    int carry=0;
    for(int i=0;i<b.size();i++){
        int t=a[a.size()-1-i]-'0'+b[b.size()-1-i]-'0'+carry;
        a[a.size()-1-i]=t%10+'0';
        carry=t/10;
    }
    for(int i=b.size();i<a.size() && carry;i++){
        int t=a[a.size()-1-i]-'0'+carry;
        a[a.size()-1-i]=t%10+'0';
        carry=t/10;
    }
    if(carry) a='1'+a;
    return a;
}

F - 拼装模型(4141)

知识点:贪心、排序、模拟
难度:★★☆☆☆


📝 思路

通常为:

  • 零件有长度或重量
  • 模型要求组合成某种形态
  • 贪心排序后尽量优先选择最优零件

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

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    int N;
    cin >> N;

    priority_queue<long long, vector<long long>, greater<long long>> pq;

    for (int i = 0; i < N; ++i) {
        long long x;
        cin >> x;
        pq.push(x);
    }

    long long ans = 0;

    while (pq.size() > 1) {
        long long a = pq.top(); pq.pop();
        long long b = pq.top(); pq.pop();
        long long c = a + b;
        ans += c;
        pq.push(c);
    }

    cout << ans;
    return 0;
}




G - Genealogical tree(4139)

知识点:拓扑排序(Kahn 法)
难度:★★☆☆☆


📝 图示(拓扑排序)

图如下:

1 → 3 → 5
↓    ↑
2 → 4

入度数组:

in[1]=0
in[2]=0
in[3]=1
in[4]=1
in[5]=1

入度=0 的节点入队,逐层 BFS。


C++(核心)

queue<int> q;
for(i=1;i<=n;i++)
    if(in[i]==0) q.push(i);
#include <bits/stdc++.h>
using namespace std;

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    int N;
    cin >> N;

    vector<vector<int>> g(N + 1);
    vector<int> indeg(N + 1, 0);

    for (int i = 1; i <= N; ++i) {
        while (true) {
            int x;
            cin >> x;
            if (x == 0) break;
            g[i].push_back(x);
            indeg[x]++;
        }
    }

    queue<int> q;
    for (int i = 1; i <= N; ++i)
        if (indeg[i] == 0) q.push(i);

    vector<int> topo;

    while (!q.empty()) {
        int u = q.front(); q.pop();
        topo.push_back(u);
        for (int v : g[u]) {
            if (--indeg[v] == 0)
                q.push(v);
        }
    }

    // 输出任意拓扑序都可
    for (int i = 0; i < (int)topo.size(); ++i) {
        cout << topo[i];
        if (i + 1 < (int)topo.size()) cout << " ";
    }
    return 0;
}

H - 怀表问题(4144)

知识点:DFS、全排列
难度:★★★☆☆


📝 题意

通常是“数字摆放 → 满足某种钟表性质”的问题。
规模小 → 用 DFS + 剪枝 枚举所有排列。


图示

      1
   12   2
 11       3
 10       4
   9    5
      6

按顺时针 DFS 排数字。


posted @ 2025-11-23 18:03  kkman2000  阅读(20)  评论(0)    收藏  举报