Evanyou Blog 彩带

P2015 二叉苹果树

题目描述

有一棵苹果树,如果树枝有分叉,一定是分2叉(就是说没有只有1个儿子的结点)

这棵树共有N个结点(叶子点或者树枝分叉点),编号为1-N,树根编号一定是1。

我们用一根树枝两端连接的结点的编号来描述一根树枝的位置。下面是一颗有4个树枝的树

2   5
 \ / 
  3   4
   \ /
    1

现在这颗树枝条太多了,需要剪枝。但是一些树枝上长有苹果。

给定需要保留的树枝数量,求出最多能留住多少苹果。

输入输出格式

输入格式:

第1行2个数,N和Q(1<=Q<= N,1<N<=100)。

N表示树的结点数,Q表示要保留的树枝数量。接下来N-1行描述树枝的信息。

每行3个整数,前两个是它连接的结点的编号。第3个数是这根树枝上苹果的数量。

每根树枝上的苹果不超过30000个。

输出格式:

一个数,最多能留住的苹果的数量。

输入输出样例

输入样例#1: 
5 2
1 3 1
1 4 10
2 3 20
3 5 20
输出样例#1: 
21

 

Solution:

  本题树形$dp$板子(不过我调了好几遍啊`~`)。

  题意要留下$q$个树枝,等同于在原树上选$q$根树枝,使得树枝必须和父节点相连,且价值最大。

  那么直接套上分组背包板子,定义状态$f[i][j]$表示在$i$的子树中选$j$条边的最大价值,则不难想到状态转移方程:$f[u][j]=max(f[u][j],f[u][j-k]+f[v][k-1]+w[u,v])$(范围$j\leq sizeu,\;k\leq min(j,sizev)$,特别注意转移时是$f[v][k-1]$(被这坑了几遍),因为在$v$子树中选边,就必须保证连通先选上$w[u,v]$这条边,所以在$v$子树中只能选$k-1$条边

  最后输出目标状态$f[n][q]$就好了。  

代码:

 

#include<bits/stdc++.h>
#define il inline
#define For(i,a,b) for(int (i)=(a);(i)<=(b);(i)++)
#define Bor(i,a,b) for(int (i)=(b);(i)>=(a);(i)--)
#define Max(a,b) ((a)>(b)?(a):(b))
#define Min(a,b) ((a)>(b)?(b):(a))

using namespace std;
const int N=205;

int n,m,rd[N],to[N],net[N],w[N],f[N][N],cnt,h[N];

il void add(int u,int v,int c){
    rd[v]++,to[++cnt]=v,net[cnt]=h[u],h[u]=cnt,w[cnt]=c;
}

il int dfs(int u,int fa){
    if(rd[u]==1&&u!=1) return 1;
    int tot=0,sizev;
    for(int i=h[u];i;i=net[i])
        if(to[i]!=fa){
            sizev=dfs(to[i],u);
            tot+=sizev;
            Bor(j,1,tot) {
                int c=Min(sizev,j);
                For(k,1,c) f[u][j]=Max(f[u][j-k]+f[to[i]][k-1]+w[i],f[u][j]);
            }
        }
    return tot+1;
}

int main(){
    ios::sync_with_stdio(0);
    cin>>n>>m;
    int u,v,c;
    For(i,1,n-1){
        cin>>u>>v>>c;
        add(u,v,c),add(v,u,c);
    }
    dfs(1,1);
    cout<<f[1][m];
    return 0;
}

 

posted @ 2018-06-11 12:30  five20  阅读(247)  评论(0编辑  收藏  举报
Live2D