codevs愚蠢的矿工(树形DP)

/*
树形DP 
根节点一定有人 然后 剩下的人没到每个孩子去
因为孩子数可能很多 不好枚举 所以转二叉树 分两部分 O(sum)就可以了
当然 转二叉树候必须顾及原来树的一些性质 如不能只选左孩子
转化好了之后就是DP了
写的记忆化 递归每个节点 枚举分给左右孩子的人数  
*/
#include<iostream>
#include<cstdio>
#include<cstring>
#define maxn 1010
using namespace std;
int n,m,son[maxn][3];
int f[maxn][maxn],a[maxn],ans;
int DP(int k,int sum)//到节点k时 还剩sum个人没有用 
{
    int i,maxx=0;
    if(f[k][sum]!=0)return f[k][sum];//记忆化 
    if(k==0||sum==0)return 0;//剪枝 
    f[k][sum]=a[k];//先整一个人在k处 
    for(i=1;i<=sum;i++)//枚举给左孩子多少人 i-1 
      {
          if(DP(son[k][2],i-1)+a[k]+DP(son[k][1],sum-i)>maxx)
          maxx=DP(son[k][2],i-1)+a[k]+DP(son[k][1],sum-i);
        if(DP(son[k][2],i)>maxx)maxx=DP(son[k][2],sum);//特殊情况 不要k 只要右孩子
        //因为这是多叉树转化来的 所以可以实现相反的 不能只要左孩子 因为左孩子和k连在一起 
      }
    if(f[k][sum]<maxx)
      f[k][sum]=maxx;
    return f[k][sum];
}
int main()
{
    cin>>n>>m;
    int i,x,y;
    for(i=1;i<=n;i++)cin>>a[i];
    for(i=1;i<=n;i++)
      {
          cin>>x>>y;
          if(son[x][1]==0)son[x][1]=y;
          else 
            {
              int fa=son[x][1];
              while(son[fa][2])fa=son[fa][2];
              son[fa][2]=y;
            }
      }
    ans=DP(son[0][1],m);
    cout<<ans;
    return 0;
}
 

 

posted @ 2016-04-25 19:30  一入OI深似海  阅读(240)  评论(0编辑  收藏  举报