Files
python/TangDou/AcWing/Section/479.cpp

67 lines
2.8 KiB
C++
Raw Normal View History

2025-08-30 18:35:01 +08:00
#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;
}