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; }
PS:~蒟蒻写博客不易,转载请注明出处,万分感谢!~