LOI 2015 二叉苹果树

题目描述

有一棵苹果树,如果树枝有分叉,一定是分2叉(就是说没有只有1个儿子的结点)
这棵树共有N个结点(叶子点或者树枝分叉点),编号为1-N,树根编号一定是1。
我们用一根树枝两端连接的结点的编号来描述一根树枝的位置。下面是一颗有4个树枝的树

2   5
 \ / 
  3   4
   \ /
    1

现在这颗树枝条太多了,需要剪枝。但是一些树枝上长有苹果。给定需要保留的树枝数量,求出最多能留住多少苹果。

分析

典型的树上dp问题,记 fij f i j 为根为 i i 的树中选择j条边最多能保留的果子数目
fij=max(fsonl,j1+wi,sonl,fsonr,j1+wi,sonr,fsonl,k+fsonr,j2k+wi,sonl+wi,sonr) f i j = m a x ( f s o n l , j − 1 + w i , s o n l , f s o n r , j − 1 + w i , s o n r , f s o n l , k + f s o n r , j − 2 − k + w i , s o n l + w i , s o n r )
其中 k k 的范围为0j2

#include <bits/stdc++.h>
#define rep( i , l , r ) for( int i = (l) ; i <= (r) ; ++i )
#define per( i , r , l ) for( int i = (r) ; i >= (l) ; --i )
#define erep( i , u ) for( int i = head[(u)] ; ~i ; i = e[i].nxt )
using namespace std;
int _read(){
    char ch = getchar();
    int x = 0 , f = 1 ;
    while( !isdigit( ch ) )
           if( ch == '-' ) f = -1 , ch = getchar();
           else ch = getchar();
    while( isdigit( ch ) )
           x = (ch  - '0') + x * 10 , ch =  getchar();
    return x * f;
}
const int maxn = 100 + 5;
struct edge{
    int v , w , nxt;
}e[maxn * 2];
int f[maxn][maxn] , head[maxn] , _t = 0 , M = 0;
inline void cmax( int &a , int b ){
    a = a > b ? a : b;
}
void addedge( int u , int v , int w ){
    e[_t].v = v , e[_t].w = w , e[_t].nxt = head[u] , head[u] = _t++;
}
void dfs( int u , int fa ){
    int son[2][2] , cnt = 0 , v;
    erep( i , u ){
        v = e[i].v;
        if( v == fa ) continue;
        son[cnt][0] = v , son[cnt][1] = e[i].w; ++cnt;
        dfs( v , u ); 
    }
    if( cnt == 0 ) return;
    rep( j , 1 , M ){
        cmax( f[u][j] , f[ son[0][0] ][j - 1] + son[0][1] );
        cmax( f[u][j] , f[ son[1][0] ][j - 1] + son[1][1] );
        if( j >= 2 )
            rep( k , 0 , j - 2 )
                cmax( f[u][j] , f[ son[0][0] ][k] + f[ son[1][0] ][j - 2 - k] + son[0][1] + son[1][1] );
    }
}
int main(){
    memset( head , 0xff , sizeof head );
    int N = _read(); M = _read();
    int u , v , w;
    rep( i , 1 , N - 1 ){
        u = _read() , v = _read() , w = _read();
        addedge( u , v , w );
        addedge( v , u , w );
    }
    dfs( 1 , 0 );
    cout << f[1][M] << endl;
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值