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经验只是一方面,更重要的还是面对实际问题时的分析和代码实现能力。