信息学奥赛一本通 1570:【例 2】能量项链 | 1843:【06NOIP提高组】能量项链 | 洛谷 P1063 [NOIP 2006 提高组] 能量项链
【题目链接】
ybt 1570:【例 2】能量项链
ybt 1843:【06NOIP提高组】能量项链
洛谷 P1063 [NOIP 2006 提高组] 能量项链
【题目考点】
1. 动态规划:区间动规
2. 环形序列
解决方法:破环为链
模板题:洛谷 P1880 [NOI1995] 石子合并
【解题思路】
本题与洛谷 P1880 [NOI1995] 石子合并十分类似,可以先做上题,而后做本题。
环形序列上的问题,首先进行破环为链,将长为n的输入a序列变为长为 2 n 2n 2n的a序列。其中 a i + n = a i , 1 ≤ i ≤ n a_{i+n}=a_i,1\le i \le n ai+n=ai,1≤i≤n。
原环形序列为由n个珠子构成的环形序列。破环为链后得到的是由 2 n − 1 2n-1 2n−1个珠子构成的线性序列。该线性序列为一个假想的 b b b序列, b b b序列的每个元素为一个珠子,第i颗珠子 d i d_i di的头标记为 a i a_i ai,尾标记为 a i + 1 a_{i+1} ai+1。最后第 2 n − 1 2n-1 2n−1个珠子 b 2 n − 1 b_{2n-1} b2n−1的头标记为 a 2 n − 1 a_{2n-1} a2n−1,尾标记为 a 2 n a_{2n} a2n。
1. 状态定义
- 阶段:第i个珠子到第j个珠子,即b序列区间 [ i , j ] [i,j] [i,j]
- 决策:合并哪两个珠子
- 策略:合并的方案
- 策略集合:b序列区间 [ i , j ] [i,j] [i,j]的所有合并方案
- 条件:获得能量最大
- 统计量:能量
状态定义: d p i , j dp_{i,j} dpi,j:b序列区间 [ i , j ] [i,j] [i,j]的所有合并方案中,获得能量最大的方案所获得的能量。
初始状态:1颗珠子无法合并,获得能量为0。因此 d p i , i = 0 dp_{i,i}=0 dpi,i=0
2. 状态转移方程
- 策略集合:b序列区间 [ i , j ] [i,j] [i,j]的所有合并方案
根据最后一次将哪两个珠子合并,来分割策略集合。
设存在分割点 k k k,将区间 [ i , j ] [i,j] [i,j]分割为区间 [ i , k ] [i,k] [i,k]与区间 [ k + 1 , j ] [k+1,j] [k+1,j]。自然 i ≤ k < j i\le k < j i≤k<j。
要想求 d p i , j dp_{i,j} dpi,j:
- 先将b序列区间 [ i , k ] [i,k] [i,k]合并为一个珠子,获得能量 d p i , k dp_{i,k} dpi,k。得到珠子的头标记为 b i b_i bi的头标记 a i a_i ai,尾标记为 b k b_k bk的尾标记 a k + 1 a_{k+1} ak+1。
- 再将b序列区间 [ k + 1 , j ] [k+1,j] [k+1,j]合并为一个珠子,获得能量 d p k + 1 , j dp_{k+1,j} dpk+1,j。得到珠子的头标记为 b k + 1 b_{k+1} bk+1的头标记 a k + 1 a_{k+1} ak+1,尾标记为 b j b_j bj的尾标记 a j + 1 a_{j+1} aj+1。
- 而后再将这两个珠子合并,获得能量 a i ⋅ a k + 1 ⋅ a j + 1 a_i\cdot a_{k+1}\cdot a_{j+1} ai⋅ak+1⋅aj+1。
求出 d p i , j = d p i , k + d p k + 1 , j + a i ⋅ a k + 1 ⋅ a j + 1 dp_{i,j} = dp_{i,k}+dp_{k+1,j}+a_i\cdot a_{k+1}\cdot a_{j+1} dpi,j=dpi,k+dpk+1,j+ai⋅ak+1⋅aj+1
枚举分割点 k k k的所有可能情况,求出每种情况下状态的最大值。
状态转移方程为:
d p i , j = max { d p i , k + d p k + 1 , j + a i ⋅ a k + 1 ⋅ a j + 1 } , i ≤ k < j dp_{i,j} = \max\{dp_{i,k}+dp_{k+1,j}+a_i\cdot a_{k+1}\cdot a_{j+1}\}, i\le k < j dpi,j=max{dpi,k+dpk+1,j+ai⋅ak+1⋅aj+1},i≤k<j
3. 具体实现
由于求满足条件的最大值,dp数组初值应该为很小的值。由于所有可能的状态值都是非负整数,因此dp数组初值可以为0。
对于长为1的区间的状态,1个珠子无法合并获得能量,因此 d p i , i = 0 dp_{i,i}=0 dpi,i=0
外层循环为区间长度len,len最小为2,最大为n。
内层循环为区间左端点i,i最小为1。由于破环为链后 b b b序列共有 2 n − 1 2n-1 2n−1个元素,i最大时,区间右端点 i + l e n − 1 i+len-1 i+len−1为 2 n − 1 2n-1 2n−1,因此 i + l e n − 1 < 2 n i+len-1<2n i+len−1<2n
取区间右端点 j = i + l e n − 1 j=i+len-1 j=i+len−1,在 i ≤ k < j i\le k <j i≤k<j范围内枚举分割点 k k k,执行状态转移方程。
最后,枚举 b b b序列上所有长为 n n n的区间,包括 [ 1 , n ] , [ 2 , n + 1 ] , . . . , [ n − 1 , 2 n − 2 ] , [ n , 2 n − 1 ] [1,n], [2, n+1], ..., [n-1, 2n-2], [n, 2n-1] [1,n],[2,n+1],...,[n−1,2n−2],[n,2n−1],即 [ i , i + n − 1 ] , 1 ≤ i ≤ n [i, i+n-1], 1\le i \le n [i,i+n−1],1≤i≤n。求出每个区间的状态值 d p i , i + n − 1 dp_{i, i+n-1} dpi,i+n−1的最大值,即为合并环形序列上珠子能获得的最大能量。
【题解代码】
解法1:区间动规 破环为链
#include<bits/stdc++.h>
using namespace std;
#define N 205
int a[N], n, ans, dp[N][N];//dp[i][j]:第i珠子到第j珠子有聚合方案中,释放的能量最大的方案的能量
int main()
{cin >> n;for(int i = 1; i <= n; ++i){cin >> a[i];a[i+n] = a[i];}for(int len = 2; len <= n; ++len)//由于都是正整数,求最大值。dp初值为很小的值,可以为0。dp[i][i] = 0 {for(int i = 1; i+len-1 < 2*n; ++i){int j = i+len-1;for(int k = i; k < j; ++k)dp[i][j] = max(dp[i][j], dp[i][k]+dp[k+1][j]+a[i]*a[k+1]*a[j+1]); }}for(int i = 1; i <= n; ++i)ans = max(ans, dp[i][i+n-1]);cout << ans;return 0;
}
``