• 洛谷 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个。

    输出格式:
    一个数,最多能留住的苹果的数量。

    输入输出样例

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

    输出样例:
    21

    题意中有隐含条件,要仔细读题,如果要选择当前边的话,那么也必须它与根节点的连线上的边也必须全部选中

    动态转移方程:(f[u][j]=max(f[u][j],f[u][j-k-1]+f[v][k]+e[i].w)(1≤j≤min(q,b[u]),0≤k≤min(b[v],j-1)))

    (u)表示当前节点,(v)为他的一颗子节点,(b)数组表示以i为根节点树上的边数

    看到这里,相信方程大家很容易就能看明白甚至自己就能想明白,但是范围为什么是这样的呢?

    我们着重讲一下这个取值范围的问题

    首先先看(k)(k)在此处表示取(v)子树上的边数,最大自然不能超过(b[v])但是为什么要小于等于(j-1)而不是(j)呢?

    我们前面提到过了,题目中是有隐含条件的,若要选取子树(v)上的边,则必须选取(u)(v)相连的边保证选取的边全部与根节点相连

    然后就是(j)(b[u])数组是在实时发生变化的,它不断加上自己子树的边,保证背包容量不断扩大,此处千万不要写成(b[v])

    下放代码

    #include<iostream>
    #include<cstring>
    #include<cstdio>
    #include<cctype>
    #define maxn 105
    #define gc() getchar()
    #define ll long long
    using namespace std;
    
    inline ll read(){
        ll a=0;char p=gc();int f=0;
        while(!isdigit(p)){f|=(p=='-');p=gc();}
        while(isdigit(p)){a=(a<<3)+(a<<1)+(p^48);p=gc();}
        return f?-a:a;
    }
    
    struct ahaha{
        int to,next,w;
    }e[maxn<<1];
    int n,q,head[maxn],f[maxn][maxn],b[maxn],sz;
    inline void add(int u,int v,int z){
        e[sz].to=v;e[sz].w=z;e[sz].next=head[u];head[u]=sz++;
    }
    void dfs(int u,int fa){
    	for(int i=head[u];~i;i=e[i].next){
    	    int v=e[i].to;if(v==fa)continue;    //防止死循环
    	    dfs(v,u);
    	    b[u]+=b[v]+1;     //当前点的边数加上子树的边数后,还需加上与子树相连的那一条边
    	    for(int j=min(q,b[u]);j>=1;--j)
    	    	for(int k=min(b[v],j-1);k>=0;--k)
    	    		f[u][j]=max(f[u][j],f[u][j-k-1]+f[v][k]+e[i].w);
    	}
    }
    
    int main(){memset(head,-1,sizeof head);
    	n=read();q=read();
    	for(int i=1;i<n;++i){
    		int x=read(),y=read(),z=read();
    		add(x,y,z);add(y,x,z);    //由于是树,每条边我们添加两遍,便于使用
    	}
    	dfs(1,0);
    	printf("%d",f[1][q]);
    	return 0;
    }
    
  • 相关阅读:
    SharePoint客户端开发:增加用户信息到用户信息列表
    Query Options的一些用法(5):日历的处理
    User Profile Service卡在Starting的解决方法
    python enumerate用法
    希腊字母的发音
    在Linux下安装go语言环境
    Gradle的安装与使用
    学习正太分布及极差、移动极差、方差、标准差等知识点
    招聘还是炫耀,设计模式是装逼利器?
    Silverlight + RIA Service的SUID的实例。
  • 原文地址:https://www.cnblogs.com/hanruyun/p/9138472.html
Copyright © 2020-2023  润新知