「题解」洛谷 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;
}