洛谷P1040 加分二叉树

@

题目

https://www.luogu.com.cn/problem/P1040
在这里插入图片描述

分析

这题被分到树形DP的题单上,但是个人感觉更像区间DP+上树的遍历.
看透了这题的情况下这题应该不难,关键就在“中序遍历为(1,2,3,...n)”,这就意味着一段连续的区间[l,r],以root为根节点,那么左子树就由[l,root-1]组成,右子树由[root+1,r]组成(联想下二叉查找树),这样,状态转移方程就很好推了。
设f[i][j]表示以区间[l,r]为一棵(子)树的能达到的最大加分,我们就可以枚举这棵树的根节点k,则有:

\[f[i][j]=max \{^{f[i][k-1]*f[k+1][j]+score[k] (i<k<j)(以中间点为根)}_{max\{^{f[i+1][j] + score[i]}_{f[i][j-1]+sorce[j]}(以左右端点为根)} \]

特别地,当i=j时f[i][j]=socre[i] (题目有讲)
以区间长度为状态,区间起点为阶段,这不就是区间DP吗?
================== 分割线 ==================
一轮DP下来,我们就知道了二叉树的最高加分,接下来就是遍历了:
DP的状态转移中,我们枚举(子)树的根节点,要是我们把它记录下来,那不就能输出前序遍历了吗?
设root[i][j]为区间[l,r]产生最高加分的根节点,并在状态转移的时候记录,根据root数组,就能写出DFS求前序遍历(注意边界等细节):

void dfs(int l , int r){//前序遍历
	if(l > r)return;
	if(l == r){
		cout << l << ' ';
		return;
	}
	int k = root[l][r];
	cout << k << ' ';
	
	dfs(l , k - 1);
	dfs(k + 1 , r);
}

难道就这么轻松愉快地AC了?
NONONO,还有一个小细节:字典序输出(题目没有明说)
这就要求我们在写DP的时候注意一下枚举k点的顺序(要严格按字典序枚举)

			f[j][r] = f[j + 1][r] + score[j] , root[j][r] = j;
			for(int k = j + 1 ; k < r ; k++){
				if(f[j][r] < f[j][k - 1] * f[k + 1][r] + score[k])
					f[j][r] = f[j][k - 1] * f[k + 1][r] + score[k] , root[j][r] = k;
			}
			if(f[j][r] < f[j][r - 1] + score[r])
				f[j][r] = f[j][r - 1] + score[r] , root[j][r] = r;

即:先枚举k为左端点的情况,再中间,最后右端点(千万不要贪方便把左右端点合并了我一开始就是这样)
然后:
真·AC
撒花

代码

#include <iostream>
#include <cstdio>
using namespace std;
int n;
int score[50];
int f[50][50];
int root[50][50];
void dfs(int l , int r){//前序遍历
	if(l > r)return;
	if(l == r){
		cout << l << ' ';
		return;
	}
	int k = root[l][r];
	cout << k << ' ';
	
	dfs(l , k - 1);
	dfs(k + 1 , r);
}
int main(){
	cin >> n;
	for(int i = 1 ; i <= n ; i++)
		cin >> score[i];
		//求最大分数
	for(int i = 1 ; i <= n ; i++)
		f[i][i] = score[i] , root[i][i] = i;
		
	for(int i = 2 ; i <= n ; i++)
		for(int j = 1 ; j <= n ; j++){
			int r = i + j - 1;
			
			f[j][r] = f[j + 1][r] + score[j] , root[j][r] = j;
			for(int k = j + 1 ; k < r ; k++){
				if(f[j][r] < f[j][k - 1] * f[k + 1][r] + score[k])
					f[j][r] = f[j][k - 1] * f[k + 1][r] + score[k] , root[j][r] = k;
			}
			if(f[j][r] < f[j][r - 1] + score[r])
				f[j][r] = f[j][r - 1] + score[r] , root[j][r] = r;
		}
	cout << f[1][n] << endl;
	dfs(1 , n);
	cout << endl;
	return 0;
} 
posted @ 2020-11-11 10:49  追梦人1024  阅读(55)  评论(0编辑  收藏  举报