题目描述 有一棵苹果树,如果树枝有分叉,一定是分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
这道题使用树形dp的思路。先把树作为无向图存储,代码如下:
int head[105],tot=0; struct e{ int v; int to; int next; }edge[205]; void add(int a,int b,int c){ edge[tot].v=c; edge[tot].to=b; edge[tot].next=head[a]; head[a]=tot++; }head[]存储是连接每个节点的第一条边在edge中的下标,通过寻找每条边的next就可以找到连接该点的每一条边。 然后再搜索所有节点并找出每个节点保留一定数量的边时最多能保留几个苹果,代码如下:
int sm[105]; int n,q,f[105][105]; void dfs(int a,int fa){ for(int i=head[a];~i;i=edge[i].next){ int to=edge[i].to; if(to==fa) continue; dfs(to,a); sm[a]+=sm[to]+1; for(int j=min(sm[a],q);j;--j){ for(int k=min(sm[to],j-1);k>=0;--k){ f[a][j]=max(f[a][j],f[a][j-k-1]+f[to][k]+edge[i].v); } } } }sm[]是这个结点下的结点总数,f[][]是动态规划数组。 先找搜索子树然后回溯再进行动态规划,a是当前搜索的结点,fa是该结点的父节点,j表示保留a的几个树枝,k表示保留子树i的几个树枝多-1是因为还有连接i的一根树枝所以也要加上i上的苹果。 下面是ac代码
#include<bits/stdc++.h> using namespace std; int head[105],tot=0; struct e{ int v; int to; int next; }edge[205]; void add(int a,int b,int c){ edge[tot].v=c; edge[tot].to=b; edge[tot].next=head[a]; head[a]=tot++; } int sm[105]; int n,q,f[105][105]; void dfs(int a,int fa){ for(int i=head[a];~i;i=edge[i].next){ int to=edge[i].to; if(to==fa) continue; dfs(to,a); sm[a]+=sm[to]+1; for(int j=min(sm[a],q);j;--j){ for(int k=min(sm[to],j-1);k>=0;--k){ f[a][j]=max(f[a][j],f[a][j-k-1]+f[to][k]+edge[i].v); } } } } int main(){ cin>>n>>q; memset(head,-1,sizeof head); int a,b,c; for(int i=0;i<n-1;i++){ cin>>a>>b>>c; add(a,b,c); add(b,a,c); } dfs(1,-1); cout<<f[1][q]<<endl; return 0; }