• ural(Timus) 1018. Binary Apple Tree


    树型DP:二叉苹果树

    另外一个提交地址:http://www.cqoi.net:2012/JudgeOnline/problem.php?id=1375

    里面有中文题目,不解释题目了。

    树型DP:主要是两点,怎么建树,怎么DP。这两者应该说是相互制约相互影响的,怎么DP就要怎么建树,而建树方法对不对也决定了你能不能DP。所以先分析怎么DP的,再分析怎么建树

    题意说要保留m个树枝,而且注意权值不是点的而是树枝的,这样子并不利于我们解决问题,我们可以转化一下,把权值转移给节点。首先给出所有边的信息后,我们的建树是唯一的(这个问题值得思考一下为什么,唯一是指不考虑左右孩子的位置,而只考虑从属关系,好像1(3,4),1(4,3)我们认为是同一棵树,这样考虑的话,建树是唯一的)

    基于树的递归性质,而且规定了1必定为树根,而建树又是唯一的,那么我们就可以采用递归的方法用1开始建树,建树部分不详说,看代码就知道是怎么回事了。另外建树时我们要把树枝的权值给节点,而一个树枝连接的其实是孩子和父亲,我们把权值给孩子。这样建树后,每个节点都会有权值,除了1,因为是整个树的根它没有父亲,所以它的权值为0.注意其他点的权值也可能为0,因为本来树枝权值可能为0,但是这不影响解题

    细想一下,一棵树中要保留m个树枝,等价于要保留m+1个点,其中一个点就是树根,即要保留m个子孙后代节点

    所以问题转化为,以1为根的树,包括1在内一共保留m+1个节点,或说以1为根保留m个子孙后代节点。注意这两者的本质是相同的,但是写法却不同,推荐前者的写法,不容易出错。下面来说说两者具体都是怎么DP的

    两者都是递归式DP,即记忆化搜索

    先说第一种:dp[rt][m]表示以rt为根,保留m个节点(包括rt自己),所以目标状态为dp[1][m+1]

    状态转移方程为 dp[rt][m] = max{ dp[lch][a] + dp[rch][b] } + val[rt];

    解释:即以左孩子为根保留a个节点(包括左孩子本身),以右孩子为根保留b个节点(包括右孩子本身),最后要加上rt这个点本身的权值。可知  a+b+1=m (注意这点)

    注意:rt=0时,即递归边界,返回0,同样的,m=0时返回0,即一个点都不保留(包括根自己)

    第二种:dp[rt][m]表示以rt为根,保留m个子孙后代节点,所以目标状态为dp[1][m]

    状态转移方程: dp[rt][m] = max { dp[lch][a]+val[lch] + dp[rch][b]+val[rch] } + val[rt]

    解释:即以左孩子为根保留a个子孙后代节点,而左孩子本身的权值也要计算进去。以右孩子保留b个子孙后代节点,而右孩子本身的权值也要计算进去,最后要加上根的权值

    可知  (a+1)+(b+1)+1=m

    注意:这样dp需要注意一些细节:rt=0,递归边界,返回0;m=0,返回val[rt],m=0表示不要保留孩子,但是还有根,所以要返回根的权; m<0,在这种DP种是会出现m<0的情况的,<0表示什么都不保留,即根都不要保留,直接删掉整个子树,返回0,因为什么节点都不保留了

    先给出第1种和第2种dp代码,方便对比,可以看完全部代码再回头看

    第1种

    int dfs(int rt ,int m)
    {
        if(rt==0 || m<0)  return 0;
        if(m==0) return dp[rt][m]=tree[rt].val;
        if(dp[rt][m]!=-1) return dp[rt][m];
    
        dp[rt][m]=0;
        for(int a=0; a<=m; a++) 
        {
            int b=m-a; 
            int sl,sr,lch,rch;
            lch=tree[rt].lch; rch=tree[rt].rch;
            sl=dfs(lch,a-1); 
            sr=dfs(rch,b-1); 
            dp[rt][m]=max(dp[rt][m] , sl+sr);
        }
        return dp[rt][m]+=tree[rt].val ;
    }

    第二种

    int dfs(int rt ,int m)
    {
        if(rt==0 || m<0)  return 0;
        if(m==0) return dp[rt][m]=tree[rt].val;
        if(dp[rt][m]!=-1) return dp[rt][m];
     
        dp[rt][m]=0;
        for(int a=0; a<=m; a++) 
        {
            int b=m-a; 
            int sl,sr,lch,rch;
            lch=tree[rt].lch; rch=tree[rt].rch;
            sl=dfs(lch,a-1); 
            sr=dfs(rch,b-1); 
            dp[rt][m]=max(dp[rt][m] , sl+sr);
        }
        return dp[rt][m]+=tree[rt].val ;
    }

    全部代码(以第1种DP)

    #include <cstdio>
    #include <cstring>
    #define N 110
    #define INF 0x3f3f3f3f
    #define max(a,b) a>b?a:b
     
    int val[N][N];
    int dp[N][N];
    struct node 
    {
        int lch,rch;
        int val ;
    }tree[N];
    bool vis[N];
     
    void build(int rt  ,int n)
    {
        vis[rt]=true;
        for(int i=1; i<=n; i++) 
            if(!vis[i] && val[rt][i]!=-1)
            {
                if(!tree[rt].lch) tree[rt].lch=i;
                else              tree[rt].rch=i;
                tree[i].val=val[rt][i]; 
                build(i,n);
            }
    }
     
    int dfs(int rt ,int m)
    {
        if(rt==0 || m<=0)  return dp[rt][m]=0; 
        if(dp[rt][m]!=-1) return dp[rt][m];
     
        dp[rt][m]=0;
        for(int a=0; a<m; a++
        {
            int b=m-1-a
            int sl,sr,lch,rch;
            lch=tree[rt].lch; rch=tree[rt].rch;
            sl=dfs(lch,a); 
            sr=dfs(rch,b); 
            dp[rt][m]=max(dp[rt][m] , sl+sr);
        }
        return dp[rt][m]+=tree[rt].val ;
    }
     
    int main()
    {
        int n,m;
        while(scanf("%d%d",&n,&m)!=EOF)
        {
            memset(val,-1,sizeof(val));
            for(int i=1; i<n; i++)
            {
                int a,b,v;
                scanf("%d%d%d",&a,&b,&v);
                val[a][b]=val[b][a]=v;
            }
            memset(vis,false,sizeof(vis));
            memset(tree,0,sizeof(tree));
            build(1,n);
            //for(int i=1; i<=n; i++) 
                //printf("%d: lch=%d rch=%d val=%d\n",i,tree[i].lch,tree[i].rch,tree[i].val);
            memset(dp,-1,sizeof(dp)); 
            dfs(1,m+1);
            printf("%d\n",dp[1][m+1]);
        }
        return 0;
    }
  • 相关阅读:
    VSCode创建自定义代码段
    生命不息,折腾不止 ~ 旧PC改造之家庭影音
    万物互联之~网络编程基础篇
    PyCharm创建自定义代码段(JetBrains系列通用)
    VSCode设置Tab键为4个空格
    Jupyter-Notebook服务器自定义密码
    Jupyter ~ 像写文章般的 Coding (附:同一个ipynb文件,执行多语言代码)
    centos下使用nohup
    在centos中创建nginx启动脚本
    查看centos中的用户和用户组
  • 原文地址:https://www.cnblogs.com/scau20110726/p/2999564.html
Copyright © 2020-2023  润新知