加分二叉树 noip

首先这肯定不是标解
树形四维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)kij』)

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);     // 前序遍历右子树
}

你可能感兴趣的:(dp)