洛谷题单指南-动态规划3-P1063 [NOIP2006 提高组] 能量项链
原题链接:https://www.luogu.com.cn/problem/P1063
题意解读:本质上是一个环形石子合并问题,计算合并产生的最大能量。
解题思路:
对于环形DP问题,可以把环拆开,并复制2倍长度,然后用1~n的区间长度去枚举
1、状态表示
设struct node {int head, tail}用于表示每一个项链节点,其中有头、尾标记值,
node s[2 * N]表示复制2倍长度的项链,
dp[i][j]表示将i ~ j的珠子合并释放的最大能量。
2、状态转移
对于i ~ j范围内每一个分界点k,有
dp[i][j] = max(dp[i][j], dp[i][k] + dp[k+1][j] + s[i].head * s[k].tail * s[j].tail)
说明:将i~j合并的能量等于将i~k合并的能量、将k+1~j合并的能量,将i~k与k+1~j整体合并的能量这三者之和,并取最大值。
3、初始化
对于dp[i][i] = 0,即一个节点不需要合并,无能量。
4、结果
所有dp[i][i+n-1]的最大值。
100分代码:
#include <bits/stdc++.h>
using namespace std;
const int N = 105;
struct node
{
int head, tail; //珠子的头、尾
};
int n;
int a[N]; //原始数据
node s[2 * N]; //珠子数量扩大2倍,环形dp转换为普通区间dp
int dp[2 * N][2 * N]; //dp[i][j]表示将i ~ j的珠子合并释放的最大能量
int ans;
int main()
{
//处理输入
cin >> n;
for(int i = 1; i <= n; i++) cin >> a[i];
for(int i = 1; i <= n; i++)
{
if(i < n) s[i] = {a[i], a[i + 1]};
if(i == n) s[i] = {a[i], a[1]};
s[i + n] = s[i];
}
//状态转移
for(int len = 2; len <= n; len++) //枚举区间长度2~n,长度为1的dp[i][i]默认为0
{
for(int i = 1; i + len - 1 <= 2 * n; i++) //枚举左端点1~n
{
int j = i + len - 1; //计算右端点
for(int k = i; k < j; k++) //枚举分界点k
{
dp[i][j] = max(dp[i][j], dp[i][k] + dp[k+1][j] + s[i].head * s[k].tail * s[j].tail);
}
}
}
for(int i = 1; i <= n; i++)
ans = max(ans, dp[i][i+n-1]);
cout << ans;
return 0;
}