题解:洛谷 P17015 消消乐

【题目来源】

洛谷:P17015 [GESP202606 七级] 消消乐 - 洛谷

【题目描述】

给定一个由 \(n\) 个整数构成的数组 \(a = [a_1, \ldots, a_n]\)。每次你可以对数组 \(a\) 进行以下操作,直到数组 \(a\) 变为空:

  • 指定 \(a\) 中的一个元素,获得该元素两侧相邻元素之和的分数,并将该元素从 \(a\) 中删去。

特别地,如果相邻元素不存在则该元素的值视为 \(0\)。例如,对于 \(a = [1, 2, 3]\) 可以进行以下操作:

  • 指定元素 \(2\),获得分数 \(1 + 3\),删去 \(2\)\(a = [1, 3]\)
  • 指定元素 \(1\),获得分数 \(0 + 3\),删去 \(1\)\(a = [3]\)
  • 指定元素 \(3\),获得分数 \(0 + 0\),删去 \(3\)\(a\) 变为空。

请问你能获得的分数总和最大是多少?

【输入】

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

第二行,\(n\) 个非负整数 \(a_1, \ldots, a_n\),表示数组 \(a\) 中的整数。

【输出】

输出一行,一个整数,表示能获得的最大分数总和。

【输入样例】

6
1 6 3 2 9 1

【输出样例】

55

【核心思想】

  1. 问题分析:给定数组 \(a_1, a_2, \ldots, a_n\),每次删除一个元素获得其两侧相邻元素之和的分数,求删除所有元素的最大分数总和。这是一个区间动态规划问题,核心在于分析删除顺序对得分的影响——最后删除的元素在得分时其相邻元素是固定的区间外边界。

  2. 算法选择

    • 区间 DP:设 \(dp[i][j]\) 为删除区间 \([i, j]\) 内所有元素能获得的最大分数
    • 最后删除策略:枚举区间 \([i, j]\) 内最后一个被删除的元素 \(k\),此时 \(k\) 的相邻元素为 \(a_{i-1}\)\(a_{j+1}\)(区间外的边界元素),得分固定
  3. 关键步骤

    • 边界处理\(a_0 = 0\)\(a_{n+1} = 0\),作为虚拟边界
    • 初始化(区间长度为 \(1\)):\(dp[i][i] = a_{i-1} + a_{i+1}\),删除单个元素 \(i\) 时得分为其两侧相邻元素之和
    • 区间 DP\(len\)\(2\)\(n\)):
      • 枚举左端点 \(i\),计算右端点 \(j = i + len - 1\)
      • 枚举最后删除的元素 \(k \in [i, j]\)
        • \(dp[i][j] = \max(dp[i][j], dp[i][k-1] + dp[k+1][j] + a_{i-1} + a_{j+1})\)
      • 解释:先分别删除 \([i, k-1]\)\([k+1, j]\) 内的元素,最后删除 \(k\),此时 \(k\) 的相邻元素为 \(a_{i-1}\)\(a_{j+1}\)
    • 输出结果\(dp[1][n]\)
  4. 时间/空间复杂度

    • 时间复杂度:\(O(n^3)\),三层循环:区间长度 \(O(n)\)、左端点 \(O(n)\)、枚举最后删除元素 \(O(n)\)
    • 空间复杂度:\(O(n^2)\),二维 \(dp\) 数组
  5. 区间动态规划的核心思想

    • 最后删除的确定性:对于区间 \([i, j]\),无论内部删除顺序如何,最后一个被删除的元素 \(k\) 在得分时,其相邻元素一定是 \(a_{i-1}\)\(a_{j+1}\)。这是因为 \([i, j]\) 内其他元素已被删除,\(k\) 的左右邻居就是区间边界外的元素
    • 子问题独立性:删除 \([i, k-1]\)\([k+1, j]\) 是两个独立的子问题,互不影响,满足最优子结构
    • 区间长度递增:按区间长度从小到大枚举,确保计算 \(dp[i][j]\) 时,所有子区间 \(dp[i][k-1]\)\(dp[k+1][j]\) 已经计算完成
    • 边界元素的固定贡献:最后删除 \(k\) 的得分 \(a_{i-1} + a_{j+1}\)\(k\) 的具体值无关,只与区间边界有关,这是状态转移的关键
    • 适用于元素删除顺序影响得分、且最后操作的相邻元素具有确定性特征的区间优化类问题

【算法标签】

普及 #区间DP

【代码详解】

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 105;                 // 常量:最大数组长度

int n;                             // n: 数组长度
int a[N];                          // a[i]: 数组元素,a[0] 和 a[n+1] 视为边界(值为0)
int dp[N][N];                      // dp[i][j]: 删除区间 [i,j] 内所有元素能获得的最大分数

signed main()
{
    cin >> n;                      // 读入数组长度

    for (int i = 1; i <= n; i++)  // 读入数组元素
        cin >> a[i];

    for (int i = 1; i <= n; i++)  // 初始化:区间长度为 1 的情况
        dp[i][i] = a[i - 1] + a[i + 1];  // 删除单个元素 i,得分 = 左侧相邻 + 右侧相邻

    for (int len = 2; len <= n; len++)  // 枚举区间长度,从 2 到 n
        for (int i = 1; i + len - 1 <= n; i++)  // 枚举区间左端点
        {
            int j = i + len - 1;    // j: 区间右端点

            for (int k = i; k <= j; k++)  // 枚举区间内最后一个被删除的元素 k
            {
                // 状态转移:先删除 [i,k-1] 和 [k+1,j] 内的元素,最后删除 k
                // 最后删除 k 时,其相邻元素为 a[i-1] 和 a[j+1](区间外的边界元素)
                dp[i][j] = max(dp[i][j], dp[i][k - 1] + dp[k + 1][j] + a[i - 1] + a[j + 1]);
            }
        }

    cout << dp[1][n] << endl;      // 输出删除整个数组 [1,n] 的最大分数

    return 0;
}

【运行结果】

6
1 6 3 2 9 1
55
posted @ 2026-07-08 10:41  团爸讲算法  阅读(12)  评论(0)    收藏  举报