AcWing
  • 首页
  • 课程
  • 题库
  • 更多
    • 竞赛
    • 题解
    • 分享
    • 问答
    • 应用
    • 校园
  • 关闭
    历史记录
    清除记录
    猜你想搜
    AcWing热点
  • App
  • 登录/注册

AcWing 320. 能量项链【区间DP】    原题链接    简单

作者: 作者的头像   一只野生彩色铅笔 ,  2021-08-04 23:39:50 ,  所有人可见 ,  阅读 6353


109


16

最近在补全提高课所有题目的题解,宣传一下汇总的地方提高课题解补全计划

题目描述

之前写的太水了,删掉了,这题的状态表示要换个角度来理解

给定 $N$ 个能量石,每个能量石有一个二元属性 $(w_{i,1}, w_{i,2})$

其中 $w_{i,1}$ 表示第 $i$ 个能量石和第 $i-1$ 个能量石融合产生的 能量 的其中一个参数

当然 $w_{i,2}$ 表示第 $i$ 个能量石和第 $i+1$ 个能量石融合产生的 能量 的其中一个参数

魔法石是顺序且环形摆放的,每次可以融合相邻两个魔法石

融合两个能量石 $i,i+1$ 所产生的能量为(题目保证,相邻能量石的参数一致,首尾一致)

$$ E_{i,i+1} = w_{i,1} \times (w_{i,2} 或 w_{i+1,1}) \times w_{i+1,1} $$

融合后左侧魔法石的第一个参数和右侧魔法石的第二个参数合并成为一颗新的 魔法石

具体如下图所示:

IMG_446B54BD8FD3-1.jpeg

求最终把所有石头融合成一个石头时,产生的最大能量值

分析

本题可以把区间长度作为搜索的阶段来进行记忆话搜索,因此我们也可以采用 区间DP 的方式来处理

这题和 环形石子合并 十分相似,但又不尽相同

在 环形石子 中,每个石头只有单一的参数,而本题有两个参数,也就意味着我们需要在细节上做出改变

经过观察我们发现,合并两个石头 $(a,b),(b,c)$ 的操作就像是矩阵乘法一样,合并完后就变成了 $(a,c)$

因此我们可以离散的来存储每个参数,具体如下所示:

IMG_E46314131354-1.jpeg

这样 状态表示 就更新为:当前合并的石子堆的左端石头的左参数是 $l$,右端石头的右参数是 $r$ 的方案

这样对应的 初始状态 本来应该是一个有着 二元属性 的石头,现在就变成了长度为 $2$ 的区间

这样合并区间后,需要记录的新石头的参数也刚好是 区间的两端 对应的参数,如下图所示:

IMG_9F6B9AD121A6-1.jpeg

而且这里我们的转移方程也要修改为 $f_{l,r} = max(f_{l,k} + f_{k,r} + E_{l,r})$

以往的 区间DP 我们是把区间 $[a,b]$ 拆分为 $[a,k] 和 [k+1,b]$

因为 同一个石子 只会被合并到 一个石子堆 里

但本题合并魔法石时,分割点 $k$ 要被分到 左侧石子堆的右端点 和 右侧石子堆的左端点 中

因此,参数 $k$ 要作为两个区间的共同端点来使用,即 $[a,k]$ 和 $[k,b]$

此外我们原来只需要合并 $n$ 个石头,这样转换后就要合并 $n+1$ 个石头了

具体分析如下所示:

闫氏DP分析法

状态表示—集合$f_{l,r}:$ 当前合并的石子堆的左端石头的左参数是 $l$,右端石头的右参数是 $r$ 的方案
状态表示—属性$f_{l,r}:$ 方案的费用最大
状态计算—$f_{l,r}:$

$$ f_{l,r} = max(f_{l,k} + f_{k,r} + E_{l,r}) \quad (l \lt k \lt r) $$

初始状态: $f_{l,l+1} = 0 \quad(1\le l \le n)$

目标状态: $f_{1,n + 1}$

本题关于如何解决 环的问题 不做额外阐述,有需要的可以参考之前写的这篇 环形石子合并

Code

时间复杂度: $O(n^3)$

#include <iostream>
#include <cstring>

using namespace std;

const int N = 110, M = N << 1;

int n, w[M], f[M][M];

int main()
{
    scanf("%d", &n);
    for (int i = 1; i <= n; ++ i) scanf("%d", &w[i]), w[n + i] = w[i];
    memset(f, -0x3f, sizeof f); //本题可以不用初始化,因为不存在负数的状态值,因此0就是最小的
    for (int len = 2; len <= n + 1; ++ len)
        for (int l = 1, r; (r = l + len - 1) <= n << 1; ++ l)
            if (len == 2) f[l][r] = 0; //初始化初始状态
            else
                for (int k = l + 1; k < r; ++ k)
                    f[l][r] = max(f[l][r], f[l][k] + f[k][r] + w[l] * w[k] * w[r]);
    int res = 0;
    for (int l = 1; l <= n; ++ l) res = max(res, f[l][l + n]);
    printf("%d\n", res);
    return 0;
}

33 评论


用户头像
frank2023   2022-07-23 21:47      20    踩      回复

wc这个作者太强了,似乎每道题的题解区排前都有他的份

用户头像
Cplusplus   2023-05-02 14:10      2    踩      回复

6

用户头像
user.banned   2023-05-30 21:35      2    踩      回复

你为什么要加“似乎”?

用户头像
张耀扬   2024-09-12 20:49      1    踩      回复

你为什么要加wc?


用户头像
user.banned   2023-05-30 21:34      1    踩      回复

给彩铅佬点赞!


用户头像
去更远的地方   2022-03-28 23:22      1    踩      回复

len = 2 为啥f [ l ] [ r ] = 0 呢? 两个球也可以合并的吧

用户头像
一只野生彩色铅笔   2022-03-28 23:49      3    踩      回复

len = 2 只有一个球

用户头像
user.banned   2023-05-31 09:24      1    踩      回复

哥,人家是多出一个元素的


用户头像
䦹徊   2024-07-13 18:04         踩      回复

精彩的题解,一看就懂


用户头像
fisherman8ywb   2024-07-08 16:20         踩      回复

if(len == 2)应该完全不用写吧,
因为len从3开始遍历就行。
合并石子那个题需要加特判的原因是它初始化了,如果不初始化成负无穷的话max也是完全不受影响的, 但min要受影响。


用户头像
回寝室睡觉了   2024-04-10 16:49         踩      回复

太强了


用户头像
污秽之翼   2023-10-17 17:13         踩      回复

%%%%%

用户头像
污秽之翼   2023-10-17 17:13         踩      回复

or2


用户头像
超人鹿   2023-07-04 10:34         踩      回复

orz


用户头像
执中   2023-04-10 19:52         踩      回复

%%%%

用户头像
Cplusplus   2023-05-02 14:10         踩      回复

6


用户头像
Beowulf   2023-03-30 22:24         踩      回复

为什么w[l]w[k]w[r]就是把区间以K分为两段以后增加的能量呢


用户头像
想个昵称好难abcd   2023-02-05 20:54         踩      回复

我还是没有想明白为什么以l+1作为隔板的话,就能保证三个珠子合成两个珠子

用户头像
o樱桃小完犊子o   2023-03-15 20:02      2    踩      回复

l是左边珠子的左端点,l + 1同时为左边珠子的右端点和右边珠子的左端点,所以每次以l + 1为媒介,就能合并左边珠子和右边珠子,

用户头像
想个昵称好难abcd   2023-03-15 20:23    回复了 o樱桃小完犊子o 的评论         踩      回复

谢谢!


用户头像
xyh2   2023-01-01 10:32         踩      回复

彩铅佬牛逼


用户头像
NERG   2022-07-07 20:45         踩      回复

为啥这里r要到达n<<1,环形石子就不需要呢

用户头像
是阿亮啊   2022-07-14 20:28         踩      回复

环形石子的范围是n,开两倍n到达n << 1其实也就是求n + 1到n << 1这里在1到n时已经求出,所以可以省去

用户头像
NERG   2022-07-14 21:59    回复了 是阿亮啊 的评论         踩      回复

^V^,3qu


用户头像
yolo222   2022-03-19 01:00         踩      回复

牛啊
感谢大佬的题解


用户头像
Karshey   2021-11-28 14:21         踩      回复

狠狠地%%%%%%%%%%%%%%


用户头像
ZCPUZZLE   2021-10-15 06:41         踩      回复

区间DP的时候,题目上说每次只能取相邻的两堆, 为什么还要枚举长度, 有dalao能给我讲一下吗

用户头像
炽热的   2021-10-21 11:16         踩      回复

怎么给你说呢,动态规划的本质就是求解子问题呀, 先把小的求出来才能求大的

用户头像
Jhsy-Augety   2021-10-26 15:44         踩      回复

你可以推导一下 f 数组实现的过程,会发现后面的 f 数组会用到前面的 f ,则前面的 f 就要先得到,所以才需要从小到大枚举长度嘛

用户头像
wuxiangzhuansheng   2022-06-23 20:31         踩      回复

一生二,二生四,四生八........


用户头像
Peter_5   2021-08-06 00:04         踩      回复

彩铅巨巨精益求精, 太赞了!!

用户头像
一只野生彩色铅笔   2021-08-06 15:31      1    踩      回复

stO Peter佬 Orz

用户头像
Peter_5   2021-08-06 15:45    回复了 一只野生彩色铅笔 的评论         踩      回复

⁄(⁄ ⁄•⁄ω⁄•⁄ ⁄)⁄


App 内打开
你确定删除吗?
1024
x

© 2018-2025 AcWing 版权所有  |  京ICP备2021015969号-2
用户协议  |  隐私政策  |  常见问题  |  联系我们
AcWing
请输入登录信息
更多登录方式: 微信图标 qq图标 qq图标
请输入绑定的邮箱地址
请输入注册信息