首先这肯定不是标解
树形四维dp
程序好理解,就是以 x为root,左子树的范围或右子书的范围,0 1判断
求前序费了不少劲,最后只得待退回去,一开始wa了一个点,是一个很隐蔽的地方,
if (s1==1&&s2==1&&(l>i-1&&i+1>r))
s=a[i];
(l>i-1&&i+1>r)这个special judge 没有,导致的
#include
using namespace std;
long long f[31][31][31][2];int a[101];
int scl(int root,int l,int r,int p)
{
int ans=0;int i;
for (i=l;i<=r;++i)
{ long long s1=f[i][l][i-1][0];
long long s2=f[i][i+1][r][1];
long long s=s1*s2+a[i];
if (s1==1&&s2==1&&(l>i-1&&i+1>r))
s=a[i];
if (s==f[root][l][r][p])
break;
}
cout<" ";
if (l<=i-1)
scl(i,l,i-1,0);
if (i+1<=r)
scl(i,i+1,r,1);
}
int dp(int x,int l,int r,int p)
{ if (p==0&&rreturn 1;
if (p&&l>r)
return 1;
if (f[x][l][r][p])
return f[x][l][r][p];
else
{ for (int i=l;i<=r;++i)
{ f[i][l][i-1][0]=dp(i,l,i-1,0);
f[i][i+1][r][1]=dp(i,i+1,r,1);
long long s=f[i][l][i-1][0]*f[i][i+1][r][1]+a[i];
if (f[i][i+1][r][1]==1&&f[i][l][i-1][0]==1)
s=a[i];
if (s>f[x][l][r][p])
{ f[x][l][r][p]=s;
}
}
}
return f[x][l][r][p];
}
int main()
{ int n;cin>>n;
for (int i=1;i<=n;++i)
cin>>a[i];
long long ans=0;
int root;
for (int i=1;i<=n;++i)
{
long long s1=dp(i,1,i-1,0);
long long s2=dp(i,i+1,n,1);
long long s=s1*s2+a[i];
if (s>ans)
root=i;
ans=max(s,ans);
}
cout<cout<" ";
scl(root,1,root-1,0);
scl(root,root+1,n,1);
}
看一下正解吧
【任务一】采用动态规划方法计算最大分值
本题可以采用动态规划方法来解决,具体如下:
设f[i, j]为顶点i . . 顶点j所组成的子树的最大分值。若f[i, j] = -1,则表明最大分值尚未计算出。
f(i,j)={1 (i>j) ; 顶点i的分数 (i=j) ; max(f{i,k-1}*f{k+1,j}+顶点i的分数 (i<j) 『k取i~j』)
root[i, j]——顶点i..顶点j所组成的子树达到最大分值时的根编号。当i = j时,root[i, i] := i。
由于问题没有明显的阶段特征,而是呈现为非线性的树形结构,因此,我们采用后序遍历的顺序来计算状态转移方程。计算过程如下:
long long search(int L, int r) // 递归计算f[L][r]
{
int k;
long long now, ans; // 当前分值
if (L > r) return 1;
if (f[L][r]== -1) // 若尚未计算出顶点L..顶点r对应子树的最高分值
for(k=L; k<=r; k++) { // 穷举每一个可能的子根k
now = search(L, k-1) * search(k+1, r) + f[k][k];
// 计算以k为根的子树的分值
if(now > f[L][r]) {
//若该分值为目前最高,则记入状态转移方程,并记下子根}
f[L][r] = now;
root[L][r] = k;
}
}
return f[L][r]; {返回顶点L..顶点r对应子树的最高分值}
}
void preorder(int L, int r)
{
if (L > r) return;
if (firstwrite)
firstwrite = false;
else
cout<<‘ ‘; // 顶点间用空格分开
cout << root[L][r]; // 输出子树的根
preorder(L, root[L][r]-1); // 前序遍历左子树
preorder(root[L][r]+1, r); // 前序遍历右子树
}