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

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

#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初始化
① 因为左、右子树为空时返回1这其实也是为计算公式准备前提按返回1计算时无左右子树的点
,也就是叶子节点时得分就是w[i]
② 此时记录的辅助路径数组g[i][i]表示的就是从i到i的区间内根是谁当然是i
Q:为什么一定要记录g[i][i]=i,不记录不行吗?一个点的区间为什么也要记录根是谁呢?
因为这是递归的出口如果不记录的话那么out输出时就会找不到出口导致死循环TLE或者MLE
*/
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;
}