[caioj 1114]多叉苹果树---树形dp+01背包

来源:互联网 发布:python 键盘输入 超时 编辑:程序博客网 时间:2024/05/16 15:50

题目描述

有一棵苹果树,如果树枝有分叉,可以是分多叉,分叉数k>=0(就是说儿子的结点数大于等于0)这棵树共有N个结点(叶子点或者树枝分叉点),编号为1~~N,树根编号一定是1。我们用一根树枝两端连接的结点的编号来描述一根树枝的位置。下面是一颗有4个树枝的树

现在这颗树枝条太多了,需要剪枝。但是一些树枝上长有苹果。给定需要保留的树枝数量,求出最多能留住多少苹果。
输入格式:
输入第一行包含两个整数n,k(1<=k 输入接下来的n – 1行,每一行包含三个整数x,y,c,表示节点x和y之间有一条树枝。c表示这根树枝上苹果的数量。
输出格式:
输出一行,为最多可以保留的苹果数。

Input
6 2
1 3 1
1 4 10
1 6 21
2 3 20
3 5 20

Output
31

数据规模:
对于20%的数据,满足1 <= n <=15。
对于40%的数据,满足1 <= n <=100。
对于100%的数据,满足1 <= n <=310。
输入
输出

分析

本题目前有两种方法,一种是题目自带的将多叉树转二叉树,在参照"二叉苹果树",还有一种类似于"比赛转播",每层进行01背包

记录每棵子树的大小
用当前的孩子v更新f[u][k]

代码

#include <cstdio>#include <cstdlib>#define open(s) freopen(s".in","r",stdin); freopen(s".out","w",stdout);#define close fclose(stdin); fclose(stdout); using namespace std;struct Edge{    int to;    int w;    int nxt;};int cnt;int head[315];Edge edge[630];inline void add(int x,int y,int z){    edge[++cnt]=(Edge){y,z,head[x]};    head[x]=cnt;}int n,m;int size[315];//节点数int f[315][315];//子树i保留j根树枝的最大值inline int read(){    int k=1;    int sum=0;    char c=getchar();    for(;'0'>c || c>'9' ;c=getchar())        if(c=='-') k=-1;    for(;'0'<=c && c<='9';c=getchar())        sum=sum*10+c-'0';    return sum*k;}inline void write(int x){    if(x<0) { putchar('-'); x*=-1; }    if(x>9) write(x/10);    putchar(x%10+'0');}inline int max(int x,int y){    return x>y?x:y;}inline int min(int x,int y){    return x<y?x:y;}inline void dfs(int u,int pre){    size[u]=1;//初始化    for(int i=head[u];i;i=edge[i].nxt)    if(edge[i].to!=pre)    {        int v=edge[i].to;        dfs(v,u);        size[u]+=size[v];        //不可移至循环外,不然会重复计算        for(int x=min(size[u],m);x;--x)//01背包,注意顺序//枚举保留孩子枝条的数量            f[u][x]=max(f[u][x],f[v][y]+f[u][x-y-1]+edge[i].w);            //保证u->v的树枝保留,故-1    }}int main(){    open("1114");    n=read();    m=read();    for(int i=1,x,y,z;i<n;++i)    {        x=read(); y=read(); z=read();        add(x,y,z);        add(y,x,z);    }    dfs(1,-1);    write(f[1][m]);    close;    return 0;}