1

GESP认证C++编程真题解析 | 202409 七级

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

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

适合人群:

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

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


编程题

P11248 矩阵移动

【题目来源】

洛谷:[P11248 GESP202409 七级] 矩阵移动 - 洛谷

【题目描述】

小杨有一个 \(n \times m\) 的矩阵,仅包含 01? 三种字符。矩阵的行从上到下编号依次为 \(1,2,\dots, n\),列从左到右编号依次为 \(1, 2, \dots, m\)。小杨开始在矩阵的左上角 \((1,1)\),小杨只能向下或者向右移动,最终到达右下角 \((n, m)\) 时停止,在移动的过程中每经过一个字符 1 得分会增加一分(包括起点和终点),经过其它字符则分数不变。小杨的初始分数为 \(0\) 分。

小杨可以将矩阵中不超过 \(x\) 个字符 ? 变为字符 1。小杨在修改矩阵后,会以最优的策略从左上角移动到右下角。他想知道自己最多能获得多少分。

【输入】

第一行包含一个正整数 \(t\),代表测试用例组数,接下来是 \(t\) 组测试用例。对于每组测试用例,一共 \(n + 1\) 行。

第一行包含三个正整数 \(n, m, x\),含义如题面所示。
之后 \(n\) 行,每行一个长度为 \(m\) 的仅含 01? 的字符串。

【输出】

对于每组测试用例,输出一行一个整数,代表最优策略下小杨的得分最多是多少。

【输入样例】

2
3 3 1
000
111
01?
3 3 1
000
?0?
01?

【输出样例】

4
2

【算法标签】

《洛谷 P11248 矩阵移动》 #动态规划DP# #GESP# #2024#

【代码详解】

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

string m1[1000];  // 存储网格地图,m1[i]表示第i行字符串
int t, n, m, dp[505][505][300], k;  // t: 测试用例数, n: 行数, m: 列数, k: 最大可使用'?'数

int main()
{
    cin >> t;  // 输入测试用例数
    
    while (t--)
    {
        // 输入网格大小和k值
        cin >> n >> m >> k;
        
        // 读入网格,并在每行前添加一个字符,使索引从1开始
        for (int i = 1; i <= n; i++)
        {
            cin >> m1[i];
            m1[i] = "e" + m1[i];  // 在字符串前添加一个字符,使得列索引从1开始
        }
        
        // 初始化动态规划数组为0
        memset(dp, 0, sizeof(dp));
        
        // 动态规划计算最大路径
        for (int i = 1; i <= n; i++)        // 遍历行
        {
            for (int j = 1; j <= m; j++)    // 遍历列
            {
                for (int b = 0; b <= k; b++)  // 遍历使用的'?'数量
                {
                    // 情况1:不使用当前位置的'?'(或当前位置是'1'或'0')
                    // 从上方或左方转移,不消耗'?'名额
                    dp[i][j][b] = (m1[i][j] == '1') + max(dp[i - 1][j][b], dp[i][j - 1][b]);
                    
                    // 情况2:如果当前位置是'?',且还有'?'名额可用
                    if (b && m1[i][j] == '?')
                    {
                        // 将'?'当作'1',消耗一个'?'名额
                        dp[i][j][b] = max(dp[i][j][b], 1 + max(dp[i - 1][j][b - 1], dp[i][j - 1][b - 1]));
                    }
                }
            }
        }
        
        // 在所有可能的'?'使用数量中,找到最大价值
        int ans = 0;
        for (int i = 0; i <= k; i++)
        {
            ans = max(ans, dp[n][m][i]);
        }
        
        cout << ans << endl;
    }
    
    return 0;
}

【运行结果】

2
3 3 1
000
111
01?
4
3 3 1
000
?0?
01?
2

P11249 小杨寻宝

【题目来源】

洛谷:P11249 [GESP202409 七级] 小杨寻宝 - 洛谷

【题目描述】

小杨有一棵包含 \(n\) 个节点的树,树上的一些节点放置有宝物。

小杨可以任意选择一个节点作为起点并在树上移动,但是小杨只能经过每条边至多一次,当小杨经过一条边后,这条边就会消失。小杨每经过一个放置有宝物的节点就会取得该宝物。

小杨想请你帮他判断自己能否成功取得所有宝物。

【输入】

本题单个测试点内有多组测试数据。输入第一行包含一个正整数 \(t\),代表测试用例组数。
接下来是 \(t\) 组测试用例。对于每组测试用例,一共 \(n+1\) 行。

第一行包含一个正整数 \(n\),代表树的节点数。
第二行包含 \(n\) 个非负整数 \(a_1, a_2, \dots a_n\),其中如果 \(a_i = 1\),则节点 \(i\) 放置有宝物;若 \(a_i = 0\),则节点 \(i\) 没有宝物。
之后 \(n - 1\) 行,每行包含两个正整数 \(x_i, y_i\),代表存在一条连接节点 \(x_i\)\(y_i\) 的边。

【输出】

对于每组测试数据,如果小杨能成功取得所有宝物,输出 Yes,否则输出 No。

【输入样例】

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

【输出样例】

Yes
No

【算法标签】

《洛谷 P11249 小杨寻宝》 #图论# #GESP# #2024#

【代码详解】

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

const int N = 100005, M = N * 2;

int T;               // 测试用例数量
int n, a[N], root;  // n: 节点数, a[i]: 节点i的初始值, root: 根节点
int h[N], e[M], ne[M], idx;  // 邻接表存储树
bool flag;          // 标志位,表示当前树是否合法

// 添加无向边
void add(int a, int b)
{
    e[idx] = b;
    ne[idx] = h[a];
    h[a] = idx++;
}

// 深度优先搜索
// u: 当前节点
// fa: 父节点
void dfs(int u, int fa)
{
    if (!flag) return;  // 如果已经发现不合法,直接返回
  
    int res = 0;  // 记录子节点值的和
  
    // 遍历所有子节点
    for (int i = h[u]; i != -1; i = ne[i])
    {
        int j = e[i];  // 子节点
        if (j == fa)   // 跳过父节点
        {
            continue;
        }
      
        // 递归处理子节点
        dfs(j, u);
      
        // 累加子节点的值
        res += a[j];
    }
  
    // 如果子节点的和不为0,将当前节点设为1
    if (res)
    {
        a[u] = 1;
    }
  
    // 检查约束条件
    if ((u == root && res > 2) || (u != root && res > 1))
    {
        flag = 0;  // 不满足条件,标记为非法
    }
}

int main()
{
    cin >> T;  // 读取测试用例数
  
    while (T--)
    {
        cin >> n;  // 读取节点数
      
        // 初始化邻接表
        memset(h, -1, sizeof(h));
        idx = 0;
        root = 0;
        flag = 1;  // 初始假设树是合法的
      
        // 读取每个节点的初始值
        for (int i = 1; i <= n; i++)
        {
            cin >> a[i];
        }
      
        // 找到值为1的节点作为根节点
        for (int i = 1; i <= n; i++)
        {
            if (a[i] == 1)
            {
                root = i;
                break;
            }
        }
      
        // 读取树的边
        for (int i = 1; i < n; i++)
        {
            int u, v;
            cin >> u >> v;
            add(u, v);
            add(v, u);
        }
      
        // 从根节点开始DFS
        dfs(root, 0);
      
        // 输出结果
        if (flag)
        {
            cout << "Yes" << endl;
        }
        else
        {
            cout << "No" << endl;
        }
    }
  
    return 0;
}

【运行结果】

2
5
0 1 0 1 0
1 2
1 3
3 4
3 5
5
1 1 1 1 1
1 2
1 3
Yes
3 4
3 5
No
posted @ 2026-01-18 00:15  热爱编程的通信人  阅读(2)  评论(0)    收藏  举报