【模板题】动态规划 [NOIP2003]加分二叉树——树形dp,区间dp

原题

题目大意:输入一棵树的中序遍历,定义一棵子树的得分为其左子树的加分×右子树的加分+根的分数。求最大得分及先序遍历

注意:
1、初始化 r[i][i]=i,便于输出
2、 初始化dp[i][i-1]=dp[i+1][i]=1。因为在区间中选取一点为root时会取到端点,即左(右)子树为空的情况,此时得分=左子树得分*1+根的分数,即给端点情况(实际这样的子树不存在)赋值为1
3、遍历顺序: 区间长度->区间起点->区间内选取根
4、区间起点的遍历n-i+1为结束,不要忘了 +1

5、区间内选取根两端点要取到

思路参考             代码参考

#include <iostream>
#include<algorithm>
#include<string.h>
using namespace std;
int n,a[31];
long long dp[27][27],r[27][27];//dp[i][j]表示中序遍历序列从i到j的最高得分
void print(int i,int j)
{
	if (i>j)
		return ;
	cout<<r[i][j]<<" ";
	print(i,r[i][j]-1);
	print(r[i][j]+1,j);
}
int main()
{
   int i,j,k;
   cin>>n;
   memset(dp,0,sizeof(0));
   for (i=1;i<=n;i++)
   {
	   cin>>a[i];
	   dp[i][i]=a[i];
	   dp[i][i-1]=1;dp[i+1][i]=1;//防止左子树(或右子树)为空的情况(虽然这个意义上的dp不存在)
	   r[i][i]=i;//初始为叶子节点(每个都为根)
   }
   for (i=2;i<=n;i++)//区间长度
	   for (j=1;j<=n-i+1;j++)//区间起点,注意截止是n-i+1
		   for (k=j;k<=j+i-1;k++)//取区间内一点为root,注意必须两头都要取到!
			   if (dp[j][j+i-1]<dp[j][k-1]*dp[k+1][j+i-1]+dp[k][k])
			   {
				   dp[j][j+i-1]=dp[j][k-1]*dp[k+1][j+i-1]+dp[k][k];
				   r[j][j+i-1]=k;
			   }
	cout<<dp[1][n]<<endl;
	print(1,n);
	cout<<endl;
    return 0;
}

猜你喜欢

转载自blog.csdn.net/Always_ease/article/details/80560501