思路不算很难,但细节处理很麻烦

前面建图、多叉转二叉,以及确定dp处理序列的过程都是套路,dp的状态转移过程以注释的形式阐述

 #include <cstdio>
 #include <cstring>
 #include <algorithm>
 #include <queue>

 int N,M,K;

 struct Edge
 {
     int to,next;
     int weight;
     void assign(int t,int n,int w)
     {
         to=t; next=n; weight=w;
     }
 };

 Edge elist[];
 ];
 ];
 ][]; //0 is left and 1 is right
 ];
 int ecnt;

 void initE()
 {
     memset(head,-,sizeof(head));
     memset(vis,,sizeof(vis));
     memset(child,-,sizeof(child));
     weight[]=;
     ecnt=;
 }

 inline void addEdge(int from,int to,int weight)
 {
     elist[ecnt].assign(to,head[from],weight);
     head[from]=ecnt++;
     elist[ecnt].assign(from,head[to],weight);
     head[to]=ecnt++;
 }

 bool input()
 {
     scanf("%d%d%d",&N,&M,&K);
     bool ok=true;
     >N) ok=false; //输出-1的情况
     initE();
     int a,b,c;
     ;i<N;i++)
     {
         scanf("%d%d%d",&a,&b,&c);
         addEdge(a,b,c);
     }
     return ok;
 }

 void buildBinaryTree(int cur)
 {
     vis[cur]=true;
     ;
     ;e=elist[e].next)
     {
         int& to=elist[e].to;
         int& w=elist[e].weight;
         if(!vis[to])
         {
             weight[to]=w;
             )
                 child[cur][]=to;
             else
                 child[last][]=to;
             last=to;
             buildBinaryTree(to);
         }
     }
 }

 ][][];
 /*
 dp[n][m][k]:处理到第n个节点时,大头已经吃掉了m个果子,其父节点将被(k=1)或不被(k=0)大头吃,此情况下的最优值
 dp[n]的处理范围:孩子兄弟二叉树中,以节点n为根的子树
 dp的初值见下方代码,ans=dp[v][K-1][1],v为孩子兄弟二叉树中1号节点的左孩子
 */

 std::queue<int> seq;

 void getSeq(int cur)
 {
     ]!=-) getSeq(child[cur][]);
     ]!=-) getSeq(child[cur][]);
     seq.push(cur);
 } //先处理右孩子,再处理左孩子,最后处理自身

 /*
 M>=3时,小头的总难受值的最小值必为0
 取两个小头A和B,奇数层的果子由A吃,偶数层的果子由B吃,这样难受值必为0
 所以只需考虑大头的难受值
 */

 int solve_aux_3()
 //若无特殊说明:solve_aux函数注释中提到的有关树的概念均指孩子兄弟二叉树,opt代指当前决策的较优值,“不吃”和“吃”均指大头
 {
     int ans;
     while(!seq.empty())
     {
         int cur=seq.front();
         seq.pop();
         );
         ]!=-) st|=;
         ]!=-) st|=;
         ) //cur是叶子节点
         {
             dp[cur][][]=dp[cur][][]=;
             dp[cur][][]=;
             dp[cur][][]=weight[cur];
         }
         ) //只有右孩子,状态转移和线性dp类似
         {
             ];
             dp[cur][][]=;
             ;i<K;i++)
                 dp[cur][i][]=std::min(dp[rc][i][],dp[rc][i-][]); //对于当前果子,opt=min(不吃,吃)
             dp[cur][][]=;
             ;i<K;i++)
                 dp[cur][i][]=std::min(dp[rc][i][],dp[rc][i-][]+weight[cur]); //opt=min(不吃,吃)
         }
         ) //只有左孩子
         {
             ];
             ) ans=dp[lc][K-][]; //最终答案
             else
             {
                 dp[cur][][]=dp[cur][][]=;
                 ;i<K;i++)
                     dp[cur][i][]=std::min(dp[lc][i][],dp[lc][i-][]); //opt=min(不吃,吃)
                 ;i<K;i++)
                     dp[cur][i][]=std::min(dp[lc][i][],dp[lc][i-][]+weight[cur]); //opt=min(不吃,吃)
             }
         }
         else //st=3,既有左孩子又有右孩子,最复杂的情况
         {
             ];
             ];
             dp[cur][][]=dp[cur][][]=;
             ;i<K;i++) //dp[cur][i][0]
             {
                 ;j<=i;j++) //不吃当前的果子
                     dp[cur][i][]=std::min(dp[lc][j][]+dp[rc][i-j][],dp[cur][i][]); //分配i,取最优的分配方案
                 ;j<i;j++) //吃当前的果子
                     dp[cur][i][]=std::min(dp[lc][j][]+dp[rc][i-j-][],dp[cur][i][]);
             }
             ;i<K;i++) //dp[cur][i][1]
             {
                 ;j<=i;j++) //不吃
                     dp[cur][i][]=std::min(dp[cur][i][],dp[lc][j][]+dp[rc][i-j][]);
                 ;j<i;j++) //吃
                     dp[cur][i][]=std::min(dp[cur][i][],dp[lc][j][]+dp[rc][i-j-][]+weight[cur]);
             }
         }
     }
     return ans;
 }

 /*
 M=2时的dp方程与M>2时形式类似,但细节上有所不同(包括初值的设置和状态转移)
 此时大头和小头的难受值必须同时考虑
 */

 int solve_aux_2()
 {
     int ans;
     while(!seq.empty())
     {
         int cur=seq.front();
         seq.pop();
         );
         ]!=-) st|=;
         ]!=-) st|=;
         )
         {
             dp[cur][][]=dp[cur][][]=;
             dp[cur][][]=dp[cur][][]=weight[cur]; //注意这里赋初值的差异
             //dp[cur][0][0]表示原树中当前节点和父节点的果子都由小头吃,所以该段树枝的难受值也必须考虑
             //类似的差异会在下方用“!”标注,请读者自行体会
         }
         )
         {
             ];
             dp[cur][][]=dp[rc][][];
             dp[cur][][]=dp[rc][][]+weight[cur]; //!
             ;i<K;i++)
             {
                 dp[cur][i][]=std::min(dp[rc][i][]+weight[cur],dp[rc][i-][]); //!
                 dp[cur][i][]=std::min(dp[rc][i][],dp[rc][i-][]+weight[cur]);
             }
         }
         )
         {
             ];
             ) ans=dp[lc][K-][];
             else
             {
                 dp[cur][][]=dp[lc][][]; //!
                 dp[cur][][]=dp[lc][][]+weight[cur];
                 ;i<K;i++)
                 {
                     dp[cur][i][]=std::min(dp[lc][i][]+weight[cur],dp[lc][i-][]); //!
                     dp[cur][i][]=std::min(dp[lc][i][],dp[lc][i-][]+weight[cur]);
                 }
             }
         }
         else
         {
             ];
             ];
             dp[cur][][]=dp[lc][][]+dp[rc][][]; //!
             dp[cur][][]=dp[lc][][]+dp[rc][][]+weight[cur];
             ;i<K;i++)
             {
                 ;j<=i;j++)
                     dp[cur][i][]=std::min(dp[cur][i][],dp[lc][j][]+dp[rc][i-j][]+weight[cur]); //!
                 ;j<i;j++)
                     dp[cur][i][]=std::min(dp[cur][i][],dp[lc][j][]+dp[rc][i-j-][]);
                 ;j<=i;j++)
                     dp[cur][i][]=std::min(dp[cur][i][],dp[lc][j][]+dp[rc][i-j][]);
                 ;j<i;j++)
                     dp[cur][i][]=std::min(dp[cur][i][],dp[lc][j][]+dp[rc][i-j-][]+weight[cur]);
             }
         }
     }
     return ans;
 }

 int solve()
 {
     memset(dp,0x3f,sizeof(dp));
     buildBinaryTree();
     getSeq();
     ?solve_aux_2():solve_aux_3();
 }

 int main()
 {
     if(!input()) printf("-1");
     else printf("%d",solve());
     ;
 }

Vijos1523 NOI2002 贪吃的九头龙 树形dp的更多相关文章

  1. [NOI2002]贪吃的九头龙(树形dp)

    [NOI2002]贪吃的九头龙 题目背景 传说中的九头龙是一种特别贪吃的动物.虽然名字叫"九头龙",但这只是 说它出生的时候有九个头,而在成长的过程中,它有时会长出很多的新头,头的 ...

  2. [codevs1746][NOI2002]贪吃的九头龙

    [codevs1746][NOI2002]贪吃的九头龙 试题描述 传说中的九头龙是一种特别贪吃的动物.虽然名字叫"九头龙",但这只是说它出生的时候有九个头,而在成长的过程中,它有时 ...

  3. vojis1523 NOI2002 贪吃的九头龙

    描述 传说中的九头龙是一种特别贪吃的动物.虽然名字叫“九头龙”,但这只是说它出生的时候有九个头,而在成长的过程中,它有时会长出很多的新头,头的总数会远大于九,当然也会有旧头因衰老而自己脱落. 有一天, ...

  4. [NOI2002] 贪吃的九头龙

    题目类型:树形DP 传送门:>Here< 题意:有一只九头龙要吃了一颗树,给出一棵\(N\)个节点的带边权的树.九头龙有\(M\)个头,其中一个是大头,大头要吃恰好\(K\)个节点,其他头 ...

  5. 洛谷 P4362 [NOI2002]贪吃的九头龙

    https://www.luogu.org/problemnew/show/P4362 首先有个很显然的dp:ans[i][j][k]表示i节点用j号头,i节点为根的子树中共有k个点用大头时i节点为根 ...

  6. Vijos1523贪吃的九头龙【树形DP】

    贪吃的九头龙 传说中的九头龙是一种特别贪吃的动物.虽然名字叫"九头龙",但这只是说它出生的时候有九个头,而在成长的过程中,它有时会长出很多的新头,头的总数会远大于九,当然也会有旧头 ...

  7. Vijos 1523 贪吃的九头龙 【树形DP】

    贪吃的九头龙 背景 安徽省芜湖市第二十七中学测试题 NOI 2002 贪吃的九头龙(dragon) Description:OfficialData:OfficialProgram:Converted ...

  8. 贪吃的九头龙(tyvj P1523)

    T2 .tyvj   P1523贪吃的九头龙 描述 传说中的九头龙是一种特别贪吃的动物.虽然名字叫“九头龙”,但这只是说它出生的时候有九个头,而在成长的过程中,它有时会长出很多的新头,头的总数会远大于 ...

  9. codevs1746 贪吃的九头龙

    [问题描述]传说中的九头龙是一种特别贪吃的动物.虽然名字叫“九头龙”,但这只是说它出生的时候有九个头,而在成长的过程中,它有时会长出很多的新头,头的总数会远大于九,当然也会有旧头因衰老而自己脱落.有一 ...

随机推荐

  1. bzoj3143

    之前我们曾经用dp解决过数学期望问题,这次我们用的是解方程的方法首先在编号之前,肯定要求出每条边的期望经过次数然后可以转化为求边端点的期望次数这种做法我一开始接触是noip2013的初赛问题求解,是类 ...

  2. bzoj3124

    很好的一道treedp,当然也挺烦的首先不难想到先求出一个直径,然后穷举每条边,看他删除之后直径大小是否会变,变了的话就是必须经过的边下面我们就要想怎么优化,本人语言表达略差,还是配合程序说吧. ty ...

  3. 数据结构(启发式合并):HNOI 2009 梦幻布丁

    Description N个布丁摆成一行,进行M次操作.每次将某个颜色的布丁全部变成另一种颜色的,然后再询问当前一共有多少段颜色.例如颜色分别为1,2,2,1的四个布丁一共有3段颜色. Input 第 ...

  4. 数据结构(莫队算法):国家集训队2010 小Z的袜子

    [题目描述] 作为一个生活散漫的人,小Z每天早上都要耗费很久从一堆五颜六色的袜子中找出一双来穿.终于有一天,小Z再也无法忍受这恼人的找袜子过程,于是他决定听天由命…… 具体来说,小Z把这N只袜子从1到 ...

  5. java基础(十三)常用类总结(三)

    这里有我之前上课总结的一些知识点以及代码大部分是老师讲的笔记 个人认为是非常好的,,也是比较经典的内容,真诚的希望这些对于那些想学习的人有所帮助! 由于代码是分模块的上传非常的不便.也比较多,讲的也是 ...

  6. 高清摄像头MIPI接口与ARM处理器的连接

    MIPI摄像头常见于手机.平板中,支持500万像素以上高清分辨率.它的全称为“Mobile Industry Processor Interface”,分为MIPI DSI 和MIPI CSI,分别对 ...

  7. 自动化运维工具Ansible详细部署 - 人生理想在于坚持不懈 - 51CTO技术博客

    自动化运维工具Ansible详细部署 - 人生理想在于坚持不懈 - 51CTO技术博客 自动化运维工具Ansible详细部署

  8. 挣值管理不是搞数字游戏(3)——进阶指标:CV、SV、CPI、SPI、EAC

    摘要: 要考PMP(Project Management Professional ),挣值管理是必考的知识.软件项目有很大的特殊性,不少人认为挣值管理不太适用于软件项目.挣值管理相关资料也比较超多, ...

  9. Graphs and Minimum Cuts(Karger's Min-Cut Algorithm)

    Graphs  Two ingredients 1. vertices (nodes) v 2. edges(undirected or directed) Examples: road networ ...

  10. css3图片滤镜

    http://www.zhanxin.info/development/2012-12-19-css-filter.html