1

GESP认证C++编程真题解析 | 202312 六级

​欢迎大家订阅我的专栏:算法题解:C++与Python实现
本专栏旨在帮助大家从基础到进阶 ,逐步提升编程能力,助力信息学竞赛备战!

专栏特色
1.经典算法练习:根据信息学竞赛大纲,精心挑选经典算法题目,提供清晰的代码实现与详细指导,帮助您夯实算法基础。
2.系统化学习路径:按照算法类别和难度分级,从基础到进阶,循序渐进,帮助您全面提升编程能力与算法思维。

适合人群:

  • 准备参加蓝桥杯、GESP、CSP-J、CSP-S等信息学竞赛的学生
  • 希望系统学习C++/Python编程的初学者
  • 想要提升算法与编程能力的编程爱好者

附上汇总帖:GESP认证C++编程真题解析 | 汇总


编程题

P10108 闯关游戏

【题目来源】

洛谷:[P10108 GESP202312 六级] 闯关游戏 - 洛谷

【题目描述】

你来到了一个闯关游戏。

这个游戏总共有 \(N\) 关,每关都有 \(M\) 个通道,你需要选择一个通道并通往后续关卡。其中,第 \(i\) 个通道可以让你前进 \(a_i\) 关,也就是说,如果你现在在第 \(x\) 关,那么选择第 \(i\) 个通道后,你将直接来到第 \(x+a_i\) 关(特别地,如果 \(x + a_i \geq N\),那么你就通关了)。此外,当你顺利离开第 \(s\) 关时,你还将获得 \(b_s\) 分。

游戏开始时,你在第 \(0\) 关。请问,你通关时最多能获得多少总分。

【输入】

第一行两个整数 \(N\)\(M\),分别表示关卡数量和每关的通道数量。

接下来一行 \(M\) 个用单个空格隔开的整数 \(a_0,a_1\cdots,a_{M-1}\)。保证 \(1\le a_i \le N\)

接下来一行 \(N\) 个用单个空格隔开的整数 \(b_0,b_1\cdots,b_{N-1}\)。保证 \(|b_i|\le 10^5\)

【输出】

一行一个整数,表示你通关时最多能够获得的分数。

【输入样例】

6 2
2 3
1 0 30 100 30 30

【输出样例】

131

【算法标签】

《洛谷 P10108 闯关游戏》 #动态规划DP# #GESP# #2023#

【代码详解】

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

const int N = 200005;  // 最大数组长度
int n, m;              // n: 天数, m: 药材种类数
int maxn = -1;         // 最大的生长天数
int a[N], b[N];        // a: 药材生长天数, b: 每天可种植的药材价值
int dp[N];             // dp[i]: 在第i天结束时的最大总价值

int main()
{
    // 输入药材种类数和总天数
    cin >> m >> n;
    
    // 输入每种药材的生长天数
    for (int i = 1; i <= m; i++)
    {
        cin >> a[i];
        maxn = max(maxn, a[i]);  // 记录最长的生长周期
    }
    
    // 输入每天可种植的药材价值
    for (int i = 1; i <= n; i++)
    {
        cin >> b[i];
    }
    
    // 初始化dp数组为极小值
    memset(dp, -0x3f, sizeof(dp));
    dp[1] = 0;  // 第1天开始时,总价值为0
    
    // 动态规划
    // 注意:这里循环到n+maxn,因为药材可能在n天后成熟
    for (int i = 1; i <= n + maxn; i++)
    {
        for (int j = 1; j <= m; j++)
        {
            // 如果i-a[j]天是有效的(≥1)
            if (i - a[j] >= 1)
            {
                // 状态转移:在第i-a[j]天种植,在第i天收获
                dp[i] = max(dp[i], dp[i - a[j]] + b[i - a[j]]);
            }
        }
    }
    
    // 调试输出
    // for (int i=1; i<=n+maxn; i++)
    //     cout << dp[i] << " ";
    // cout << endl;
    
    // 寻找最大价值
    // 只需要考虑n天之后到n+maxn天之间的最大值
    int ans = -1e9;
    for (int i = n + 1; i <= n + maxn; i++)
    {
        ans = max(ans, dp[i]);
    }
    
    // 输出结果
    cout << ans << endl;
    
    return 0;
}

【运行结果】

6 2
2 3
1 0 30 100 30 30
131

P10109 工作沟通

【题目来源】

洛谷:[P10109 GESP202312 六级] 工作沟通 - 洛谷

【题目描述】

某公司有 \(N\) 名员工,编号从 \(0\)\(N-1\)。其中,除了 \(0\) 号员工是老板,其余每名员工都有一个直接领导。我们假设编号为 \(i\) 的员工的直接领导是 \(f_i\)

该公司有严格的管理制度,每位员工只能受到本人或直接领导或间接领导的管理。具体来说,规定员工 \(x\) 可以管理员工 \(y\),当且仅当 \(x=y\),或 \(x=f_y\),或 \(x\) 可以管理 \(f_y\)。特别地,\(0\) 号员工老板只能自我管理,无法由其他任何员工管理。

现在,有一些同事要开展合作,他们希望找到一位同事来主持这场合作,这位同事必须能够管理参与合作的所有同事。如果有多名满足这一条件的员工,他们希望找到编号最大的员工。你能帮帮他们吗?

【输入】

第一行一个整数 \(N\),表示员工的数量。

第二行 \(N - 1\) 个用空格隔开的正整数,依次为 \(f_1,f_2,\dots f_{N−1}\)

第三行一个整数 \(Q\),表示共有 \(Q\) 场合作需要安排。

接下来 \(Q\) 行,每行描述一场合作:开头是一个整数 \(m\)\(2 \le m \le N\)),表示参与本次合作的员工数量;接着是 \(m\) 个整数,依次表示参与本次合作的员工编号(保证编号合法且不重复)。

保证公司结构合法,即不存在任意一名员工,其本人是自己的直接或间接领导。

【输出】

输出 \(Q\) 行,每行一个整数,依次为每场合作的主持人选。

【输入样例】

5
0 0 2 2
3 
2 3 4
3 2 3 4
2 1 4 

【输出样例】

2
2 
0

【算法标签】

《洛谷 P10109 工作沟通》 #图论# #图遍历# #GESP# #2023#

【代码详解】

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

const int N = 305;  // 最大节点数
int n, q;           // n: 节点数, q: 查询次数
vector<int> g[N];  // 邻接表存储树
bool st[N];        // 访问标记数组

// 深度优先搜索,标记从u出发能到达的所有节点
void dfs(int u)
{
    st[u] = 1;  // 标记当前节点
    for (int i = 0; i < g[u].size(); i++)
    {
        st[g[u][i]] = 1;     // 标记子节点
        dfs(g[u][i]);        // 递归遍历
    }
}

int main()
{
    // 输入节点数
    cin >> n;
    
    // 输入树的n-1条边
    for (int i = 1; i < n; i++)
    {
        int x;
        cin >> x;            // 节点i的父节点
        g[x].push_back(i);   // 建立从父节点x到子节点i的边
    }
    
    // 输入查询次数
    cin >> q;
    
    // 处理每个查询
    while (q--)
    {
        int m;  // 查询中包含的节点数
        cin >> m;
        vector<int> ve;  // 存储查询节点
        
        // 输入查询节点
        for (int i = 1; i <= m; i++)
        {
            int x;
            cin >> x;
            ve.push_back(x);
        }
        
        // 从大到小遍历节点编号
        for (int i = n - 1; i >= 0; i--)
        {
            // 重置访问标记
            memset(st, 0, sizeof(st));
            
            // 从节点i开始DFS,标记所有可达节点
            dfs(i);

            // 检查是否所有查询节点都被标记
            bool flag = 1;
            for (int j = 0; j < ve.size(); j++)
            {
                if (st[ve[j]] == false)  // 有节点未标记
                {
                    flag = 0;
                    break;
                }
            }
            
            // 如果所有查询节点都在i的子树中
            if (flag)
            {
                cout << i << endl;  // 输出当前节点编号
                break;              // 找到答案,退出循环
            }
        }
    }
    
    return 0;
}

【运行结果】

5
0 0 2 2
3
2 3 4
2
3 2 3 4
2
2 1 4
0
posted @ 2026-01-19 17:23  热爱编程的通信人  阅读(0)  评论(0)    收藏  举报