思路:不管它是序列型DP还是树形DP,反正就是DP了。我们记f[l,r]为从l至r这一段序列构成的加分二叉树的最大值,那么f[l,r]的值来自于三种情况,即l是根,或r是根,或k(k∈(l,r))是根,说白了就是要在l至r这一段序列中枚举根的位置求最大值。由于我们求的时候要递归到单个节点上再从下往上回溯求解各个f[l,r],所以f[l,r]仅与它下一级的f[l’,r’]有关,不会也不会被上一级影响,这也是我们能使用DP的原因。至于实现起来,为了防止超时,我们把已经得出的结果加以记录,需要用时直接return该值,所以看起来更像是记忆化搜索。为了输出路径,我们只需在求解各f[l,r]的过程中记录下最终确定的树根并记录于root[l,r],最后先序遍历输出即可。
代码如下:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long LL;
const int maxn=35;
int n,a[maxn],root[maxn][maxn];
LL f[maxn][maxn];
void init()
{
scanf("%d",&n);
for (int i=1;i<=n;++i)
scanf("%d",&a[i]);
memset(f,-1,sizeof(f));
memset(root,0,sizeof(root));
for (int i=1;i<=n;++i)
{
f[i][i]=a[i];
root[i][i]=i;
}
}
LL dp(int l,int r)
{
int tmproot=0;
if (l>r)
return f[l][r]=1;
if (f[l][r]!=-1)
return f[l][r];
f[l][r]=-1;
for (int i=l;i<=r;++i)
{
LL ans1=dp(l,i-1);
LL ans2=dp(i+1,r);
if (ans1*ans2+a[i]>f[l][r])
{
f[l][r]=ans1*ans2+a[i];
root[l][r]=i;
}
}
return f[l][r];
}
void front(int l,int r)
{
if (l==r)
{
printf("%d ",root[l][l]);
return;
}
int mid=root[l][r];
printf("%d ",mid);
if (mid==l)
{
front(l+1,r);
}
else if (mid==r)
{
front(l,r-1);
}
else
{
front(l,mid-1);
front(mid+1,r);
}
}
int main()
{
init();
printf("%lld\n",dp(1,n));
front(1,n);
return 0;
}
版权声明:本文为博主原创文章,未经博主允许不得转载。
原文地址:http://blog.csdn.net/little_flower_0/article/details/47377759