题解:洛谷 P17015 [GESP202606 七级] 消消乐
·
【题目来源】
洛谷:P17015 [GESP202606 七级] 消消乐 - 洛谷
【题目描述】
给定一个由 n n n 个整数构成的数组 a = [ a 1 , … , a n ] a = [a_1, \ldots, a_n] a=[a1,…,an]。每次你可以对数组 a a a 进行以下操作,直到数组 a a a 变为空:
- 指定 a a a 中的一个元素,获得该元素两侧相邻元素之和的分数,并将该元素从 a a a 中删去。
特别地,如果相邻元素不存在则该元素的值视为 0 0 0。例如,对于 a = [ 1 , 2 , 3 ] a = [1, 2, 3] a=[1,2,3] 可以进行以下操作:
- 指定元素 2 2 2,获得分数 1 + 3 1 + 3 1+3,删去 2 2 2 后 a = [ 1 , 3 ] a = [1, 3] a=[1,3];
- 指定元素 1 1 1,获得分数 0 + 3 0 + 3 0+3,删去 1 1 1 后 a = [ 3 ] a = [3] a=[3];
- 指定元素 3 3 3,获得分数 0 + 0 0 + 0 0+0,删去 3 3 3 后 a a a 变为空。
请问你能获得的分数总和最大是多少?
【输入】
第一行,一个正整数 n n n,表示数组长度。
第二行, n n n 个非负整数 a 1 , … , a n a_1, \ldots, a_n a1,…,an,表示数组 a a a 中的整数。
【输出】
输出一行,一个整数,表示能获得的最大分数总和。
【输入样例】
6
1 6 3 2 9 1
【输出样例】
55
【核心思想】
-
问题分析:给定数组 a 1 , a 2 , … , a n a_1, a_2, \ldots, a_n a1,a2,…,an,每次删除一个元素获得其两侧相邻元素之和的分数,求删除所有元素的最大分数总和。这是一个区间动态规划问题,核心在于分析删除顺序对得分的影响——最后删除的元素在得分时其相邻元素是固定的区间外边界。
-
算法选择:
- 区间 DP:设 d p [ i ] [ j ] dp[i][j] dp[i][j] 为删除区间 [ i , j ] [i, j] [i,j] 内所有元素能获得的最大分数
- 最后删除策略:枚举区间 [ i , j ] [i, j] [i,j] 内最后一个被删除的元素 k k k,此时 k k k 的相邻元素为 a i − 1 a_{i-1} ai−1 和 a j + 1 a_{j+1} aj+1(区间外的边界元素),得分固定
-
关键步骤:
- 边界处理: a 0 = 0 a_0 = 0 a0=0, a n + 1 = 0 a_{n+1} = 0 an+1=0,作为虚拟边界
- 初始化(区间长度为 1 1 1): d p [ i ] [ i ] = a i − 1 + a i + 1 dp[i][i] = a_{i-1} + a_{i+1} dp[i][i]=ai−1+ai+1,删除单个元素 i i i 时得分为其两侧相邻元素之和
- 区间 DP( l e n len len 从 2 2 2 到 n n n):
- 枚举左端点 i i i,计算右端点 j = i + l e n − 1 j = i + len - 1 j=i+len−1
- 枚举最后删除的元素 k ∈ [ i , j ] k \in [i, j] k∈[i,j]:
- d p [ i ] [ j ] = max ( d p [ i ] [ j ] , d p [ i ] [ k − 1 ] + d p [ k + 1 ] [ j ] + 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}) dp[i][j]=max(dp[i][j],dp[i][k−1]+dp[k+1][j]+ai−1+aj+1)
- 解释:先分别删除 [ i , k − 1 ] [i, k-1] [i,k−1] 和 [ k + 1 , j ] [k+1, j] [k+1,j] 内的元素,最后删除 k k k,此时 k k k 的相邻元素为 a i − 1 a_{i-1} ai−1 和 a j + 1 a_{j+1} aj+1
- 输出结果: d p [ 1 ] [ n ] dp[1][n] dp[1][n]
-
时间/空间复杂度:
- 时间复杂度: O ( n 3 ) O(n^3) O(n3),三层循环:区间长度 O ( n ) O(n) O(n)、左端点 O ( n ) O(n) O(n)、枚举最后删除元素 O ( n ) O(n) O(n)
- 空间复杂度: O ( n 2 ) O(n^2) O(n2),二维 d p dp dp 数组
-
区间动态规划的核心思想:
- 最后删除的确定性:对于区间 [ i , j ] [i, j] [i,j],无论内部删除顺序如何,最后一个被删除的元素 k k k 在得分时,其相邻元素一定是 a i − 1 a_{i-1} ai−1 和 a j + 1 a_{j+1} aj+1。这是因为 [ i , j ] [i, j] [i,j] 内其他元素已被删除, k k k 的左右邻居就是区间边界外的元素
- 子问题独立性:删除 [ i , k − 1 ] [i, k-1] [i,k−1] 和 [ k + 1 , j ] [k+1, j] [k+1,j] 是两个独立的子问题,互不影响,满足最优子结构
- 区间长度递增:按区间长度从小到大枚举,确保计算 d p [ i ] [ j ] dp[i][j] dp[i][j] 时,所有子区间 d p [ i ] [ k − 1 ] dp[i][k-1] dp[i][k−1] 和 d p [ k + 1 ] [ j ] dp[k+1][j] dp[k+1][j] 已经计算完成
- 边界元素的固定贡献:最后删除 k k k 的得分 a i − 1 + a j + 1 a_{i-1} + a_{j+1} ai−1+aj+1 与 k k 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
更多推荐
所有评论(0)