GESP C++ 六级 DP 精选 4 题题解

本文档精选 4 道 GESP 六级真题。每道题包含:题目大意、状态定义、转移方程、边界条件、完整 AC 代码。


第 1 题:[GESP202506 六级] 学习小组

题目大意

有 n 名学生,编号 1~n 连续编号。将这些学生分成若干个连续编号的小组,每个小组人数不能超过 m,且每个小组有对应的奖励值。求分组后奖励值之和的最大值。

  • 输入:第一行两个整数 n, m;第二行 n 个整数 a[i],a[i] 表示 i 个人组成的小组的奖励值。

思路分析

经典线性 DP(钢条切割模型)

  • 状态定义:dp[i] 表示前 i 名学生分组的最大奖励值之和。
  • 转移方程:枚举最后一个小组有 j 个人(1 ≤ j ≤ min(m, i)):

\[dp[i] = \max_{1 \le j \le \min(m,i)} \{ dp[i-j] + a[j] \} \]

  • 边界条件:dp[0] = 0(0 个学生,奖励为 0)。
  • 计算顺序:从小到大 i = 1 → n。
  • 时间复杂度:O(nm)。

AC 代码

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

const int MAXN = 2005;
int n, m;
long long a[MAXN];
long long dp[MAXN];

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    
    cin >> n >> m;
    for (int i = 1; i <= n; i++) {
        cin >> a[i];
    }
    
    dp[0] = 0;
    for (int i = 1; i <= n; i++) {
        dp[i] = 0;
        for (int j = 1; j <= min(m, i); j++) {
            dp[i] = max(dp[i], dp[i - j] + a[j]);
        }
    }
    
    cout << dp[n] << '\n';
    return 0;
}

第 2 题:[GESP202603 六级] 选数

题目大意

有 n 个格子排成一排,每个格子 i 上有数字 a[i],并且每个格子有一个跳跃距离 b[i]。游戏规则:

  1. 初始站在第 1 格。
  2. 到达第 i 格时,可以选择:
    • 拿取该格子的数字 a[i],然后向后跳跃 b[i] 格;
    • 不拿,向后走 1 格。
  3. 走到第 n+1 格时游戏结束。

求能获得的最大数字之和。

思路分析

一维跳跃 DP(从后往前递推)

  • 状态定义:dp[i] 表示到达第 i 格时,从第 i 格出发到游戏结束能获得的最大数字之和。
  • 转移方程:
    • 不拿:dp[i] = dp[i + 1]
    • 拿:dp[i] = a[i] + dp[i + b[i]](若 i+b[i] > n,则后面没有值)
  • 边界条件:dp[n+1] = 0。
  • 计算顺序:从后往前 i = n → 1。

AC 代码

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

const int MAXN = 100005;
int n;
int a[MAXN], b[MAXN];
long long dp[MAXN];

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    
    cin >> n;
    for (int i = 1; i <= n; i++) {
        cin >> a[i];
    }
    for (int i = 1; i <= n; i++) {
        cin >> b[i];
    }
    
    dp[n + 1] = 0;
    for (int i = n; i >= 1; i--) {
        // 不选,走一步
        dp[i] = dp[i + 1];
        // 选,跳 b[i] 步
        if (i + b[i] <= n) {
            dp[i] = max(dp[i], (long long)a[i] + dp[i + b[i]]);
        } else {
            dp[i] = max(dp[i], (long long)a[i]);
        }
    }
    
    cout << dp[1] << '\n';
    return 0;
}

第 3 题:[GESP202509 六级] 划分字符串

题目大意

给定一个由小写字母组成的字符串 s,长度为 n。要求将字符串划分成若干段,每一段中所有字符均不相同。每划分为一段长为 k 的子串,得分为 k²。求划分后得分之和的最大值。

思路分析

划分型 DP

  • 状态定义:dp[i] 表示字符串前 i 个字符划分后的最大得分。
  • 转移方程:枚举最后一段的起始位置 j,向前推直到遇到重复字符:

\[dp[i] = \max_{j}\{ dp[j] + (i-j)^2 \} \]

  • 边界条件:dp[0] = 0。
  • 技巧:用 vis 数组记录当前子串中出现过的字符,遇到重复就 break。
  • 计算顺序:i = 1 → n。

AC 代码

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

const int MAXN = 2005;
int n;
string s;
long long dp[MAXN];
bool vis[26];

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    
    cin >> s;
    n = s.size();
    s = " " + s;  // 下标从 1 开始
    
    dp[0] = 0;
    for (int i = 1; i <= n; i++) {
        memset(vis, 0, sizeof(vis));
        dp[i] = 0;
        // 向前枚举最后一段
        for (int j = i - 1; j >= 0; j--) {
            int c = s[j + 1] - 'a';
            if (vis[c]) {
                break;  // 出现重复字符,停止扩展
            }
            vis[c] = true;
            int len = i - j;
            dp[i] = max(dp[i], dp[j] + (long long)len * len);
        }
    }
    
    cout << dp[n] << '\n';
    return 0;
}

第 4 题:[GESP202503 六级] 环线

题目大意

n 个格子排成一个环形,每个格子上有一个数(可能为负数)。请选择一段连续的格子(可以环绕一圈),使得选中的数字之和最大。求这个最大值。注意:至少选一个格子。

思路分析

环形最大子段和(拆环技巧)

分两种情况讨论:

  • 情况 1:最大子段和不跨越首尾(普通线性)
    → 直接求普通最大子段和:cur_max = max(a[i], cur_max + a[i])
  • 情况 2:最大子段和跨越首尾
    → 等价于:总和 - 普通最小子段和
    → ans2 = sum - min_sub
  • 答案:max(ans1, ans2)

⚠️ 特殊情况:如果所有数都是负数,必须取情况 1(选最大的负数)。

AC 代码

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

const int MAXN = 200005;
int n;
long long a[MAXN];

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    
    cin >> n;
    long long sum = 0;
    for (int i = 1; i <= n; i++) {
        cin >> a[i];
        sum += a[i];
    }
    
    // 最大子段和(情况 1)
    long long max_sub = a[1], cur_max = a[1];
    // 最小子段和(情况 2)
    long long min_sub = a[1], cur_min = a[1];
    long long max_val = a[1];
    
    for (int i = 2; i <= n; i++) {
        cur_max = max(a[i], cur_max + a[i]);
        max_sub = max(max_sub, cur_max);
        
        cur_min = min(a[i], cur_min + a[i]);
        min_sub = min(min_sub, cur_min);
        
        max_val = max(max_val, a[i]);
    }
    
    long long ans;
    if (max_val < 0) {
        // 所有数都是负数,选最大的一个
        ans = max_val;
    } else {
        ans = max(max_sub, sum - min_sub);
    }
    
    cout << ans << '\n';
    return 0;
}

三、DP 模型速查表

模型 核心状态 典型转移 代表题
线性 DP dp[i] dp[i] = max(dp[i-j] + w[j]) 学习小组
跳跃 DP dp[i] dp[i] = a[i] + dp[i+b[i]] 选数
划分 DP dp[i] dp[i] = max(dp[j] + w(j+1,i)) 划分字符串
环形 DP 拆成两种情况 max(不跨环, 总和-最小子段) 环线
posted @ 2026-09-04 17:00  费先生  阅读(63)  评论(0)    收藏  举报