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

编程题

P15800 选数

【题目来源】

洛谷:[P15800 GESP202603 六级] 选数 - 洛谷

【题目描述】

给定两个包含 \(n\) 个整数的数组 \(a=[a_1,\dots,a_n]\)\(b=[b_1,\dots,b_n]\)。你需要指定若干下标 \(p_1\lt \cdots\lt p_k\)\(1\leq k\leq n\))使得以下条件成立:

  • \(1\leq p_i\leq n\)\(1\leq i\leq k\));
  • \(p_{i+1}\geq p_i+b_{p_i}\)\(1\leq i< k\))。

你需要在满足以上条件的前提下最大化 \(\sum_{i=1}^k a_{p_k}\),也即最大化数组 \(a\) 对应下标的整数之和。

【输入】

第一行,一个正整数 \(n\),表示数组长度。

第二行,\(n\) 个正整数 \(a_1,a_2,\dots,a_n\),表示数组 \(a\)

第三行,\(n\) 个正整数 \(b_1,b_2,\dots,b_n\),表示数组 \(b\)

【输出】

一行,一个整数,表示在满足下标条件的前提下,数组 \(a\) 对应下标的整数之和的最大值。

【输入样例】

4
1 2 3 4
3 3 1 1

【输出样例】

7

【算法标签】

《洛谷 P15800 选数》 #动态规划DP# #GESP# #2026#

【代码详解】

// 40分版本
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 100005;

int n, a[N], b[N], ans;
// dp[i] 表示以第 i 个活动结尾时,能获得的最大价值
int dp[N];
int len = 0;

signed main()
{
    cin >> n;
    for (int i = 1; i <= n; i++)
    {
        cin >> a[i];
    }
    for (int i = 1; i <= n; i++)
    {
        cin >> b[i];
    }

    for (int i = 1; i <= n; i++)
    {
        // 初始化:只选择第 i 个活动
        dp[i] = a[i];
        // 考虑在 i 之前的活动 j
        for (int j = 1; j < i; j++)
        {
            // 检查活动 j 结束后是否可以安排活动 i
            if (i >= j + b[j])
            {
                // 如果可以,则尝试从活动 j 转移到活动 i
                dp[i] = max(dp[i], dp[j] + a[i]);
            }
        }
    }

    // 找到所有 dp[i] 中的最大值
    for (int i = 1; i <= n; i++)
    {
        ans = max(ans, dp[i]);
    }

    cout << ans << endl;
    return 0;
}
// 100分版本
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 100005;

int n, a[N], b[N];
// dp[i]: 到时间i为止能获得的最大收益(不包含在时间i开始的任务)
int dp[N];

signed main()
{    
    cin >> n;
    for (int i = 1; i <= n; i++)
    {
        cin >> a[i];
    }
    for (int i = 1; i <= n; i++)
    {
        cin >> b[i];
    }
    
    int ans = 0;
    
    for (int i = 1; i <= n; i++)
    {
        // 情况1: 在时间i开始执行任务i
        // 收益 = 到时间i为止的最大收益 + 任务i的收益
        ans = max(ans, dp[i] + a[i]);
        
        // 如果执行任务i
        int finish_time = i + b[i];
        if (finish_time <= n)
        {
            // 在任务i结束时更新收益
            dp[finish_time] = max(dp[finish_time], dp[i] + a[i]);
        }
        
        // 情况2: 在时间i不执行任何任务
        // 收益延续到下一个时间点
        dp[i + 1] = max(dp[i + 1], dp[i]);
    }
    
    // 注意:最终答案不一定是dp[n],因为可能在n时刻之前就已经得到了最大收益
    // 我们已经在循环中通过ans记录了所有可能的最大值
    cout << ans << endl;
    
    return 0;
}

【运行结果】

4
1 2 3 4
3 3 1 1
7

P15801 完全二叉树

【题目来源】

洛谷:[P15801 GESP202603 六级] 完全二叉树 - 洛谷

【题目描述】

给定一棵包含 \(n\) 个结点的有根二叉树,结点依次以 \(1,2,\dots,n\) 编号,根结点编号为 \(1\)

对于结点 \(i\),其左儿子的编号记为 \(l_i\),右儿子编号记为 \(r_i\)。特别地,如果左儿子不存在则 \(l_i=0\),如果右儿子不存在则 \(r_i=0\)

树中每个结点都对应一棵以其为根的子树。请你求出给定有根树的所有 \(n\) 棵子树中,有多少棵子树是完全二叉树。

【输入】

第一行,一个正整数 \(n\),表示有根二叉树结点数量。

接下来 \(n\) 行,每行两个正整数 \(l_i,r_i\),表示结点 \(i\) 的左儿子编号和右儿子编号。

【输出】

输出一行,一个整数,表示所有子树中完全二叉树的数量。

【输入样例】

4
2 3
4 0
0 0
0 0

【输出样例】

4

【解题思路】

image

【算法标签】

《洛谷 P15801 完全二叉树》 #树形数据结构# #树形DP# #树的遍历# #GESP# #2026#

【代码详解】

#include <bits/stdc++.h>
using namespace std;
const int N = 100005;

int n, ans;
struct tree
{
    int l, r, cnt, dep, flag;
} tr[N];

void dfs(int u)
{
    int lchild = tr[u].l, rchild = tr[u].r;
    // 如果是叶子节点
    if (lchild == 0 && rchild == 0)
    {
        tr[u].cnt = 1;  // 节点计数为1
        tr[u].dep = 1;  // 深度为1
        tr[u].flag = 1; // 标记为完全二叉树
        ans++;          // 计数加1
        return;
    }
    
    tr[u].cnt = 1;  // 初始化计数,包含当前节点自身
    
    // 遍历左子树
    if (tr[u].l)
    {
        dfs(tr[u].l);
        tr[u].cnt += tr[lchild].cnt;  // 累加左子树的节点数
        tr[u].dep = tr[lchild].dep + 1;  // 计算深度
    }
    
    // 遍历右子树
    if (tr[u].r)
    {
        dfs(tr[u].r);
        tr[u].cnt += tr[rchild].cnt;  // 累加右子树的节点数
        tr[u].dep = max(tr[u].dep, tr[rchild].dep + 1);  // 更新最大深度
    }
    
    // 处理只有一个子节点的情况
    if (tr[lchild].dep == 1 && tr[rchild].dep == 0)
    {
        tr[rchild].flag = 1;
    }
    
    // 如果左右子树都是完全二叉树
    if (tr[lchild].flag == 1 && tr[rchild].flag == 1)
    {
        // 情况1: 左右子树深度相等
        if (tr[lchild].dep == tr[rchild].dep && tr[lchild].cnt == (1 << tr[lchild].dep) - 1)
        {
            tr[u].flag = 1;  // 当前节点是完全二叉树的根
            ans++;
        }
        // 情况2: 左子树深度比右子树大1
        else if (tr[lchild].dep == tr[rchild].dep + 1 && tr[rchild].cnt == (1 << tr[rchild].dep) - 1)
        {
            tr[u].flag = 1;  // 当前节点是完全二叉树的根
            ans++;
        }
    }
}

int main()
{
    cin >> n;
    for (int i = 1; i <= n; i++)
    {
        int l, r;
        cin >> l >> r;
        tr[i].l = l, tr[i].r = r;
    }
    dfs(1);
    cout << ans << endl;
    return 0;
}

【运行结果】

4
2 3
4 0
0 0
0 0
4
posted @ 2026-03-19 15:17  团爸讲算法  阅读(29)  评论(0)    收藏  举报