P1040 [NOIP2003 提高组] 加分二叉树

算法要素:二叉树的中序&前序遍历+区间dp

思路分析:

二叉树中序遍历的顺序是:左->根->右
因此对于原题目给出的区间\((1,n)\),其根节点一定在\((1,n)\)上。

同时,二叉树具有合并的性质,即不断合并子节点可以得到最终的根节点,
而区间dp也是通过不断地合并区间得到最终的答案\(dp[1][n]\)
可以合并这一性质而言,二叉树或者其他树,与区间dp高度一致,
因此就可以用区间dp解决。

至于要求输出前序遍历,只要单开一个数组\(root[i][j]\)表示\((i,j)\)的最优解情况下的根节点。
最终递归输出即可

tips:答案范围很大,因此要开\(long long\)

Code

#include<bits/stdc++.h>
using namespace std;
const int maxn=34;
int n;
long long a[maxn];
long long dp[maxn][maxn],root[maxn][maxn];
void print(int l,int r)
{
	if(l>r) return;
	printf("%lld ",root[l][r]);
	if(l==r) return;
	print(l,root[l][r]-1);
	print(root[l][r]+1,r); 
}	
int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;++i) scanf("%lld",&a[i]);
	for(int i=1;i<=n;++i) dp[i][i]=a[i],dp[i][i-1]=1,root[i][i]=i;
	for(int len=2;len<=n;++len)
	{
		for(int i=1;i+len-1<=n;++i)
		{
			int r=i+len-1;
			for(int k=i;k<=r;++k)
			{
				if(dp[i][r]<dp[i][k-1]*dp[k+1][r]+a[k])
				{
					dp[i][r]=dp[i][k-1]*dp[k+1][r]+a[k];
					root[i][r]=k;		
				}
			}
		}
	}
	printf("%lld\n",dp[1][n]);
	print(1,n);
	return 0;
}

经验总结:

练习今天上午的模拟赛T4,很容易发现:
(1)一些具有可合并性质的问题可以转化为二叉树或者其他树的形式。
其根节点代表最终所求的答案。

(2)二叉树或者其他树,与区间dp均具有可合并性这一特点,
因此二叉树或者其他树的一些问题可以转化成区间dp解决。

至于是什么样的问题。。大概就是最值或者其他dp常见问题。
emm经验只是一方面,更重要的还是面对实际问题时的分析和代码实现能力。

posted @ 2021-11-04 23:08  Mint-hexagram  阅读(80)  评论(0编辑  收藏  举报