320. 能量项链(环形,区间dp)

这篇博客介绍了如何使用动态规划解决AcWing题库中的能量项链问题,该问题涉及到将珠子上的标记合并,目标是最小化标记数量。博主详细解析了状态表示、状态计算和递推公式,并提供了AC代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 320. 能量项链 - AcWing题库

分析:可以看成一个环形的区间dp问题

一共有4个珠子,如图有四个标记,可以合并到最后只剩下2个标记,也就是1个珠子为止。

那么最少是需要3个标记才可以继续合并,如果小于三个标记就不用再合并了。

状态表示:所有合并i~j个标记的集合

状态计算/划分依据:根据i~j中间的标记可以划分为不重不漏的j-i-1个集合,由于i,j表示的是标记,所以不能参与合并(dp[i][i]不是一个珠子,不参与运算,所以dp[i][j],j>=i+1)

递推公式是dp[i][k]+dp[k][j],因为断点前一个集合k作为尾标记,后一个集合k作为头标记

  • i+1作为中断点,合并dp[i][i+1],dp[i+1][j],dp[i][j]=dp[i][i+1]+dp[i+1][j]+w[i]*w[i+1]*w[j]
  • ...
  • j-1作为中断点,合并dp[i][j-1],dp[j-1][j],dp[i][j]=dp[i][j-1]+dp[j-1][j]+w[i]*w[j-1]*w[j]

初始化和边界均为0

递推方式

for(int len=1;len<=n+1;len++)//由于右端点可能是起点,所以<=n+1
//因为最后一个珠子的尾标记是第一个标记
        for(int i=1;i+len-1<=2*n;i++)
        {
            ...
        }

ac代码

#include <iostream>
#include <algorithm>
using namespace std;
const int N=205;
const int INF=0x3f3f3f3f;
int a[N];
int s[N];
int dp[N][N];
int main()
{
    int n;
    cin>>n;
    for(int i=1;i<=n;i++)
        cin>>a[i],a[n+i]=a[i];
    
    for(int len=1;len<=n+1;len++)
        for(int i=1;i+len-1<=2*n;i++)
        {
            int j=i+len-1;
            for(int k=i+1;k<j;k++)
                dp[i][j]=max(dp[i][j],dp[i][k]+dp[k][j]+a[i]*a[k]*a[j]);
        }
    int res=0;
    for(int i=1;i<=n;i++)
        res=max(res,dp[i][i+n]);
    cout<<res;
}

### 能量项链算法的C++实现 能量项链问题可以通过动态规划解决。以下是基于状态转移方程 `dp[i][j] = max{dp[i][k] + dp[k+1][j] + a[i]*a[k+1]*a[j+1]}, i≤k<j` 的 C++ 实现。 #### 动态规划核心思路 该问题的核心在于找到一种最优的聚合顺序,使得最终的能量最大化。通过定义二维数组 `dp[i][j]` 表示从第 `i` 颗珠子到第 `j` 颗珠子的最大能量值,可以逐步计算出全局最优解[^1]。 #### 边界条件与初始化 对于单颗珠子的情况(即 `i == j`),其能量为零,因此初始时设置 `dp[i][i] = 0`。 #### 状态转移过程 遍历区间长度 `len` 和起点位置 `i`,并通过枚举分割点 `k` 来更新当前区间的最大能量值。具体实现如下: ```cpp #include <iostream> #include <vector> #include <algorithm> using namespace std; int main() { int n; cin >> n; // 输入珠子的数量 vector<int> beads(n + 2); // 使用n+2来处理环形结构 for (int i = 1; i <= n; ++i) { cin >> beads[i]; } // 复制前两个元素到最后,形成闭环效果 beads[n + 1] = beads[1]; beads[n + 2] = beads[2]; const int INF = 0; vector<vector<int>> dp(n + 2, vector<int>(n + 2, INF)); // 初始化边界条件 for (int i = 1; i <= n + 2; ++i) { dp[i][i] = 0; } // 枚举区间长度 for (int len = 2; len <= n; ++len) { // 区间长度从2开始 for (int i = 1; i + len - 1 <= n + 1; ++i) { // 左端点 int j = i + len - 1; // 右端点 if (j >= n + 2) continue; // 超过有效范围跳过 for (int k = i; k < j; ++k) { // 断点 dp[i][j] = max(dp[i][j], dp[i][k] + dp[k + 1][j] + beads[i] * beads[k + 1] * beads[j + 1]); } } } // 计算结果并取最大值 int result = 0; for (int i = 1; i <= n; ++i) { result = max(result, dp[i][i + n - 1]); // 找到最大的能量值 } cout << result << endl; // 输出结果 return 0; } ``` 上述代码实现了能量项链问题中的动态规划求解方法,并考虑了环形结构的影响。通过复制部分数据模拟环状排列,从而简化了逻辑复杂度[^2]。 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值