zoukankan      html  css  js  c++  java
  • 洛谷 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;
    }
    
  • 相关阅读:
    进程池,线程池,协程,gevent模块,协程实现单线程服务端与多线程客户端通信,IO模型
    线程相关 GIL queue event 死锁与递归锁 信号量l
    生产者消费者模型 线程相关
    进程的开启方式 进程的join方法 进程间的内存隔离 其他相关方法 守护进程 互斥锁
    udp协议 及相关 利用tcp上传文件 socketserver服务
    socket套接字 tcp协议下的粘包处理
    常用模块的完善 random shutil shevle 三流 logging
    day 29 元类
    Django入门
    MySQL多表查询
  • 原文地址:https://www.cnblogs.com/hanruyun/p/9138472.html
Copyright © 2011-2022 走看看