能量项链 区间DP题解

题目描述

原题来自:NOIP 2006

在 Mars 星球上,每个 Mars 人都随身佩带着一串能量项链。在项链上有 N N N 颗能量珠。能量珠是一颗有头标记和尾标记的珠子,这些标记对应着某个正整数。并且,对于相邻的两颗珠子,前一颗珠子的尾标记必定等于后一颗珠子的头标记。因为只有这样,通过吸盘——Mars 人吸收能量的器官的作用,这两颗珠子才能聚合成一颗珠子,同时释放出可被吸盘吸收的能量。如果一颗能量珠头标记为 m m m,尾标记为 r r r,后一颗能量珠头标记为 r r r,尾标记为 n n n,则聚合后释放出 m × r × n m \times r \times n m×r×n Mars单位的能量,新珠子头标记为 m m m,尾标记为 n n n
当需要时,Mars 人就用吸盘夹住相邻的两颗珠子,通过聚合得到能量,直到项链上只剩下一颗珠子为止。显然,不同的聚合顺序得到的总能量是不一样的。请设计一个聚合顺序使得一串珠子聚合后释放出的总能量最大。
现在给你一串项链,项链上有 n n n 颗珠子,相邻两颗珠子可以合并成一个,合并同时会放出一定的能量,不同珠子合并放出能量不相同,请问按怎样的次序合并才能使得释放的能量最多?

输入格式

第一行一个正整数 n ( n ≤ 100 ) n(n \leq 100) n(n100)
第二行 n n n 个不超过 1000 1000 1000 的正整数,第 i ( 1 ≤ 1 ≤ n ) i(1 \leq 1 \leq n) i(11n) 个数为第 i i i 颗珠子的头标记。
注意这是一个环。至于珠子的顺序,你可以这样确定:将项链放在桌面上,不要出现交叉,随机指定一颗珠子为第一颗珠子,按顺时针确定其它珠子的顺序。

输出格式

输出只有一行,一个不超过 2.1 × 1 0 9 2.1\times10^9 2.1×109 的正整数,表示最优聚合顺序所释放的能量。

样例

输入
4
2 3 5 10
输出
710

分析

易知这是一道区间DP。令 d p [ i ] [ j ] dp[i][j] dp[i][j]为合并 i i i j j j之间的项链所能释放的最大能量,即最后 i i i j j j 之间只能剩下一条项链。由区间DP的思想可知 d p [ i ] [ j ] dp[i][j] dp[i][j] 一定是由 d p [ i ] [ k ] dp[i][k] dp[i][k] d p [ k + 1 ] [ j ] dp[k + 1][j] dp[k+1][j]合并而来的。 ( i ≤ k < j ) (i \leq k \lt j) (ik<j)
于是 d p [ l ] [ r ] = m a x ( d p [ l ] [ r ] , d p [ l ] [ k ] + d p [ k + 1 ] [ r ] + a [ l ] . X ∗ a [ r ] . Y ∗ a [ k ] . Y ) ; dp[l][r] = max(dp[l][r], dp[l][k] + dp[k + 1][r] + a[l].X * a[r].Y * a[k].Y); dp[l][r]=max(dp[l][r],dp[l][k]+dp[k+1][r]+a[l].Xa[r].Ya[k].Y);
又因为此题为一个,我们不知道序列的头和尾在哪,这种时候一般可以把转化为长度为 2 n 2n 2n

代码

#include <cstdio>
#include <algorithm>
#include <climits>
#include <cmath>
#include <cstring>
using namespace std;
const int MAXN = 205;
int n, dp[MAXN][MAXN];
struct Node {
    int X, Y;
} a[MAXN];
int main() {
    int r, maxx = 0;
    scanf("%d", &n);
    for (int i = 1; i <= n; i++) {
        scanf("%d", &a[i].X);
    }
    for (int i = 1; i <= n; i++) {
        if (i != n)
            a[i].Y = a[i + 1].X;
        else {
            a[i].Y = a[1].X;
        }
        a[i + n].X = a[i].X;
        a[i + n].Y = a[i].Y;
    }
    for (int len = 2; len <= n; len++) {
        for (int l = 1; l <= (2 * n - len + 1); l++) {
            r = l + len - 1;
            for (int k = l; k < r; k++) {
                dp[l][r] = max(dp[l][r], dp[l][k] + dp[k + 1][r] + a[l].X * a[r].Y * a[k].Y);
            }
        }
    }
    for (int i = 1; i <= n; i++) {
        maxx = max(maxx, dp[i][i + n - 1]);
    }
    printf("%d", maxx);
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值