洛谷 P1040 加分二叉树
题目传送门
其实看到正解是 区间DP 时还是很震惊的。
但是从题面来看,他说给出中序遍历再让你求出加分最大的情况下的先序遍历,还是可以从 二叉树的重构 中汲取些灵感的。
1.二叉树的重构
先不管什么情况下加分最大,假如现在就给你二叉树的中序遍历,让你输出它的先序遍历所有可能情况的数量(其实就是问你一个中序遍历所对应的二叉树有多少种,即二叉树的重构)。
这其实也是一道 区间DP。
在中序遍历中,每个编号都可以作为根,如:
1
2
3
4
5
\space1\space2\space3\space4\space5
1 2 3 4 5
1
1
1 可以为根,那么它就 没有左子树,所有
1
1
1 右边的数都是它 右子树中的节点;
3
3
3 可以为根,它的左子树就是它左边所有节点,它的右子树就是它所有右边的节点;
…
\dots
…
以此类推。
发现根节点的的子树也是一段区间,那么这个对应的区间,一定也有一个 对应的子树的根节点。
初见递归端倪。
因此假设
d
p
(
i
,
j
)
dp(i,j)
dp(i,j) 为这段区间的 重构二叉树 的数量,那么枚举这段区间的根节点
k
k
k,就可以得到递推式:
d
p
(
i
,
j
)
=
(
∑
k
=
i
+
1
j
−
1
d
p
(
i
,
k
−
1
)
∗
d
p
(
k
+
1
,
j
)
)
+
d
p
(
i
+
1
,
j
)
+
d
p
(
i
,
j
−
1
)
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,j)=(k=i+1∑j−1dp(i,k−1)∗dp(k+1,j))+dp(i+1,j)+dp(i,j−1)
特别的:
d
p
(
i
,
i
)
=
1
dp(i,i)=1
dp(i,i)=1,即只有一个节点时,它的重构二叉树只有一个。
之所以把 d p ( i + 1 , j ) + d p ( i , j − 1 ) dp(i+1,j)+dp(i,j-1) dp(i+1,j)+dp(i,j−1) 单拿出来,是因为根为 i i i 或 j j j 时,没有左子树或右子树。
2.终于入题:区间DP
设
d
p
[
i
]
[
j
]
dp[i][j]
dp[i][j] 表示该区间所能获得的最大加分,那么很容易写出转移方程:
d
p
[
i
]
[
j
]
=
m
a
x
k
=
i
j
d
p
[
i
]
[
k
−
1
]
∗
d
p
[
k
+
1
]
[
j
]
+
a
[
k
]
dp[i][j]=max^{j}_{k=i}{dp[i][k-1]*dp[k+1][j]+a[k]}
dp[i][j]=maxk=ijdp[i][k−1]∗dp[k+1][j]+a[k]
特殊处理
k
=
i
k=i
k=i 和
k
=
j
k=j
k=j 时,左子树或右子树加分为
1
1
1 的情况。
至于输出先序遍历,用一个数组
r
t
rt
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;
}
更多推荐
所有评论(0)