洛谷 P1040 加分二叉树(动态规划,二叉树)

动态规划,二叉树
本题要点:
1、状态表示:
dp[i][j] 表示 区间 [i,j] 的数作为二叉树,得到的最大得分
2、边界:
dp[i][i] = d[i]; // 只有一个点构成二叉树,最大得分就是该点的分值。
3、转态转移:
dp[i][j] = max{dp[k][k] + dp[i][k - 1] * dp[k + 1][j]}
区间[i,j] 构成的二叉树,假设以 k 点为二叉树的根(i <= k <= j), 扫描所有的根,看看谁的得分最大。
dp[1][n], 表示的就是 题目要求的最大总得分。
4、 root[i][j] 表示 [i,j] 构成的二叉树,谁是根节点。 题目要求输出,这颗二叉树的先序遍历顺序时候,
写个递归即可。

#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
const int MaxN = 110;
int n;
int root[MaxN][MaxN];	// root[i][j] 表示 [i,j] 构成的二叉树,谁是根节点
long long d[MaxN];
long long dp[MaxN][MaxN];// dp[i][j] 表示 区间 [i,j] 的数作为二叉树,得到的得分
int ans[MaxN], cnt;

void inOrder(int i, int j)
{
	if(i > j)
		return;
	if(i == j)
	{
		ans[++cnt] = root[i][j];
		return;
	}
	ans[++cnt] = root[i][j];
	inOrder(i, root[i][j] - 1);
	inOrder(root[i][j] + 1, j);
}

void solve()
{
	for(int i = 1; i <= n; ++i)
		for(int j = 1; j < i; ++j)
			dp[i][j] = 1;
	for(int i = 1; i <= n; ++i)
	{
		dp[i][i] = d[i];
		root[i][i] = i;	// 第i 号节点是根节点
		if(i + 1 <= n)
			dp[i][i + 1] = d[i] + d[i + 1], root[i][i + 1] = i;
	}
	long long tmp;
	for(int step = 2; step < n; ++step)
	{
		for(int i = 1; i + step <= n; ++i)
		{
			int j = i + step;
			for(int k = i; k <= j; ++k)	// 以k为根的二叉树,计算得分
			{
				tmp = dp[k][k] + dp[i][k - 1] * dp[k + 1][j];
				if(tmp > dp[i][j])
				{
					dp[i][j] = tmp;
					root[i][j] = k;
				}
			}
		}
	}
	printf("%lld\n", dp[1][n]);
	inOrder(1, n);
	for(int i = 1; i <= n; ++i)
	{
		printf("%d", ans[i]);
		if(i + 1 <= n)
			printf(" ");
		else
			printf("\n");
	}
}

int main()
{
	scanf("%d", &n);
	for(int i = 1; i <= n; ++i)
		scanf("%lld", &d[i]);
	solve();
	return 0;
}

/*
5
5 7 1 2 10
*/

/*
145
3 1 2 4 5
*/
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值