「题解」洛谷 P1063 能量项链

题目

P1063 能量项链

简化题意

给你一个环,环上每个点有点权,每次能合并两个相邻的点,得到的贡献是两点点权的乘积再乘上靠后的点的下一个点的点权,合并出来的新点的点权是靠前的点的点权。问你最大贡献是多少。

题解

区间 dp。

\(a_i\) 表示点 \(i\) 的点权。

\(f_{i,j}\) 表示合并了 \([i,j]\) 所能得到的最大贡献。

状态转移方程如下:

\(f_{i,j} = \max(f_{i,j},f_{i,k} + f_{k + 1,j} + a[i] * a[k + 1] * a[j + 1])\)

表示合并 \([i,j]\) 这个区间是先合并了 \([i,k]\)\([k + 1,j]\)这两个区间然后进行的合并。

Code

#include <cstdio>
#include <cstring>
#include <string>
#include <iostream>
#include <algorithm>
#define M 201

int max(int a, int b) { return a > b ? a : b; }

int n, a[M], b[M], f[M][M];

int main() {
    scanf("%d", &n);
    for (int i = 1; i <= n; ++i) {
        scanf("%d", &a[i]);
        a[n + i]  = a[i];
    }
    for (int i = 1; i <= 2 * n; ++i) b[i] = a[i % n + 1];
    memset(f, 0, sizeof f);
    for (int l = 1; l <= n; ++l) {
        for (int i = 1; i + l - 1 <= 2 * n; ++i) {
            int j = i + l - 1;
            for (int k = i; k < j; ++k) {
                f[i][j] = max(f[i][j], f[i][k] + f[k + 1][j] + a[i] * b[k] * b[j]);
            }
        }
    }
    int ans = 0;
    for (int i = 1; i <= n; ++i) {
        ans = max(ans, f[i][i + n - 1]);
    }
    std::cout << ans << '\n';
    return 0;
}
posted @ 2020-09-13 17:11  yu__xuan  阅读(104)  评论(0编辑  收藏  举报