加分二叉树 noip

来源:互联网 发布:unity编程 编辑:程序博客网 时间:2024/05/20 18:42

首先这肯定不是标解
树形四维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<iostream>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<<i<<" ";   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&&r<l)     return 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<<ans<<endl;   cout<<root<<" ";   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);     // 前序遍历右子树}
2 1
原创粉丝点击