洛谷 P1040 加分二叉树
题目传送门
其实看到正解是 区间DP 时还是很震惊的。
但是从题面来看,他说给出中序遍历再让你求出加分最大的情况下的先序遍历,还是可以从 二叉树的重构 中汲取些灵感的。
1.二叉树的重构
先不管什么情况下加分最大,假如现在就给你二叉树的中序遍历,让你输出它的先序遍历所有可能情况的数量(其实就是问你一个中序遍历所对应的二叉树有多少种,即二叉树的重构)。
这其实也是一道 区间DP。
在中序遍历中,每个编号都可以作为根,如:
\(\space1\space2\space3\space4\space5\)
\(1\) 可以为根,那么它就 没有左子树,所有 \(1\) 右边的数都是它 右子树中的节点;
\(3\) 可以为根,它的左子树就是它左边所有节点,它的右子树就是它所有右边的节点;
\(\dots\)
以此类推。
发现根节点的的子树也是一段区间,那么这个对应的区间,一定也有一个 对应的子树的根节点。
初见递归端倪。
因此假设 \(dp(i,j)\) 为这段区间的 重构二叉树 的数量,那么枚举这段区间的根节点 \(k\),就可以得到递推式:
\[dp(i,j)=(\sum^{j-1}_{k=i+1}{dp(i,k-1)*dp(k+1,j)})+dp(i+1,j)+dp(i,j-1)
\]
特别的:\(dp(i,i)=1\),即只有一个节点时,它的重构二叉树只有一个。
之所以把 \(dp(i+1,j)+dp(i,j-1)\) 单拿出来,是因为根为 \(i\) 或 \(j\) 时,没有左子树或右子树。
2.终于入题:区间DP
设 \(dp[i][j]\) 表示该区间所能获得的最大加分,那么很容易写出转移方程:
\[dp[i][j]=max^{j}_{k=i}{dp[i][k-1]*dp[k+1][j]+a[k]}
\]
特殊处理 \(k=i\) 和 \(k=j\) 时,左子树或右子树加分为 \(1\) 的情况。
至于输出先序遍历,用一个数组 \(rt\) 记录转移就好了。
#include <bits/stdc++.h>
#define mkpr make_pair
namespace FIO {
template<typename T>
T read() {
T x = 0, f = 1; char c = getchar();
while (c < '0' || c > '9') {if (c == '-') f = -1; c = getchar();}
while (c >= '0' && c <= '9') {x = (x << 3) + (x << 1) + (c ^ 48); c = getchar();}
return x * f;
}
template<typename T>
void print(T x) {
if (x < 0) x = -x, putchar('-');
if (x > 9) print(x / 10);
putchar(x % 10 + 48);
}
}
using namespace FIO;
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef __int128 INT;
typedef pair<int, int> pii;
const int maxn = 3e3 + 7;
const int inf = 0x3f3f3f3f;
int n, a[maxn];
ll dp[maxn][maxn];
int rt[maxn][maxn];
void print_path(int l, int r) {
if (!rt[l][r]) return ;
print(rt[l][r]), putchar(' ');
print_path(l, rt[l][r] - 1);
print_path(rt[l][r] + 1, r);
}
int main() {
n = read<int>();
for (int i = 1; i <= n; ++i)
dp[i][i] = a[i] = read<int>(), rt[i][i] = i;
for (int l = 2; l <= n; ++l) {
for (int i = 1; i <= n; ++i) {
int j = i + l - 1;
for (int k = i; k <= j; ++k) {
if (k == i) {
if (dp[i][j] < 1 * dp[k + 1][j] + a[k]) {
dp[i][j] = 1 * dp[k + 1][j] + a[k];
rt[i][j] = k;
}
} else if (k == j) {
if (dp[i][j] < 1 * dp[i][k - 1] + a[k]) {
dp[i][j] = 1 * dp[i][k - 1] + a[k];
rt[i][j] = k;
}
} else {
if (dp[i][j] < dp[i][k - 1] * dp[k + 1][j] + a[k]) {
dp[i][j] = dp[i][k - 1] * dp[k + 1][j] + a[k];
rt[i][j] = k;
}
}
}
}
}
print(dp[1][n]), putchar('\n');
print_path(1, n), putchar('\n');
return 0;
}

浙公网安备 33010602011771号