区间DP——加分二叉树

11 篇文章 0 订阅
7 篇文章 0 订阅

区间DP——加分二叉树

原题链接

这道题目给了我们中序遍历,在这里我们需要用到一个结论:

一颗子树的中序遍历在给定的中序遍历中,一定是连续的一段。

因此,这里我们进行状态表示:

d p [ L , R ] dp[L,R] dp[L,R]表示所有中序遍历是 [ L , R ] [L,R] [L,R]这一段的二叉树的集合、

对于状态计算,由于一段中序遍历可能会对应多个不同的二叉树,因此,我们在这里比较容易区分的最后一个不同点就是每颗子树的根,也就是,我们按照根节点的不同对集合进行划分。

对于 d p [ L , R ] dp[L,R] dp[L,R]这个集合来说,可以分成以L为根节点,以L + 1为根节点……,以R为根节点这么多不同的集合划分。因此,对于 [ L , R ] [L,R] [L,R]我们可以枚举根节点,确定出最大值。

假设我们现在枚举的根节点是K,那么该树的最大值就是:

d p [ L , K − 1 ] ∗ d p [ K + 1 , R ] + w [ K ] dp[L,K-1]*dp[K+1,R]+w[K] dp[L,K1]dp[K+1,R]+w[K]

也就是两个子树的最大值的积再加上当前此根节点的权值。这样问题其实就已经分解成了区间DP问题。下面一个重要的问题就是怎么能够把当前根节点进行记录,也就是记录方案。

我们采用一个数组进行存储。用 r o o t [ L , R ] root[L,R] root[L,R]来记录当前这个区间我们选取的根节点。

我们首先确定了 r o o t [ 1 , N ] root[1,N] root[1,N],这样我们可以递归处理左右子树,分别找出左右子树选取的根节点。

#include <bits/stdc++.h>
using namespace std;

int w[35], root[35][35], dp[35][35];
void dfs(int l, int r) {
	if (l > r) return;
	cout << root[l][r] << " ";
    // 由于输出的是前序遍历,递归输出左右子树选取的根节点
	dfs(l, root[l][r] - 1);
	dfs(root[l][r] + 1, r);
}
int main() 
{
	ios::sync_with_stdio(false);
	cin.tie(0), cout.tie(0);
	int n;
	cin >> n;
	for (int i = 1; i <= n; i++) {
		cin >> w[i];
	}
	for (int len = 1; len <= n; len++) {
		for (int l = 1; l + len - 1 <= n; l++) {
			int r = l + len - 1;
			if (l == r) { // 叶子节点
				dp[l][r] = w[l];
				root[l][r] = l;
			}
			else {
				for (int k = l; k <= r; k++) {
					int left = k == l ? 1 : dp[l][k - 1]; // 左子树的最大值
					int right = k == r ? 1 : dp[k + 1][r]; // 右子树的最大值
					if (dp[l][r] < left * right + w[k]) { // 保证字典序最小
						dp[l][r] = left * right + w[k];
						root[l][r] = k;
					}
				}
			}
		}
	}
	cout << dp[1][n] << endl;
	dfs(1, n);
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值