You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

67 lines
2.8 KiB

2 years ago
#include <bits/stdc++.h>
using namespace std;
const int N = 50;
int n;
int w[N]; // 权值数组
int f[N][N]; // DP数组 : i,j区间内的最大得分
int g[N][N]; // 路径数组i,j区间内的最大得分是在k这个节点为根的情况下获得的
// 前序遍历输出路径
void out(int l, int r) {
/*
l=r,
,g[l][r] = l = r
[l,g[l][r]-1],[g[l][r]+1,r]
[l,l-1],[r+1,r]
*/
if (l > r) return;
cout << g[l][r] << " "; // 输出根结点,前序遍历
out(l, g[l][r] - 1); // 递归左子树
out(g[l][r] + 1, r); // 递归右子树
}
int main() {
scanf("%d", &n);
for (int i = 1; i <= n; i++) scanf("%d", &w[i]);
/* DP初始化
11
,w[i]
g[i][i]iii
Q:g[i][i]=i,
outTLEMLE
*/
for (int i = 1; i <= n; i++)
f[i][i] = w[i], g[i][i] = i;
// 区间DP
for (int len = 2; len <= n; len++) // 单个节点组成的区间都是初值搞定了,我们只讨论两个长度的区间
for (int l = 1; l + len - 1 <= n; l++) { // 枚举左端点
int r = l + len - 1; // 计算右端点
// 枚举根节点k, 两个区间:[l,k-1],[k+1,r],根节点k也占了一个位置
// 注意k是可以取到r的原因论述见题解
for (int k = l; k <= r; k++) {
// 根据题意特判
int ls = k == l ? 1 : f[l][k - 1]; // 左子树为空返回1
int rs = k == r ? 1 : f[k + 1][r]; // 右子树为空返回1
// 得分计算公式
int t = ls * rs + w[k];
// 记录取得最大值时的根节点k
if (f[l][r] < t) {
f[l][r] = t;
g[l][r] = k; // 记录l~r区间的最大得分是由哪个根节点k转化而来
}
}
}
// 输出
cout << f[1][n] << endl;
// 利用递归,输出字典序路径
out(1, n);
return 0;
}