题目描述
有一棵苹果树,如果树枝有分叉,一定是分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.
本来以为就是常规的和选课差不多的基本树形DP.
于是想出来一个思路:
f [i][j] 表示当前以当前这个节点为根的子树,删掉了 j 条边的最大值.
然后我想的是先预处理每一个节点的边之和和果子之和,结果发现妥妥的后效性...
于是,想到常规思路.不过为了取消对于当前这个边是否为当前这个子树的根的连边的考虑.
f [i][j] 表示当前这个节点一定被保存下来.
然后的话状态转移也就很简单了,就是一个类似于背包的转移方程.
但是,我打的时候,之前一直是 0 .
结果发现,我对已经初始化了的 f[x][1],仍然进行了更新...
代码
#include<bits/stdc++.h> using namespace std; const int maxn=105; struct sj{ int to; int next; int w; }a[maxn*2]; int size,head[maxn]; int c[maxn],n,m,num[maxn]; int f[maxn][maxn],v[maxn]; void add(int x,int y,int z) { a[++size].to=y; a[size].next=head[x]; head[x]=size; a[size].w=z; } /*void pre(int x) { for(int i=head[x];i;i=a[i].next) { int tt=a[i].to; if(!c[tt]&&tt!=1) { pre(tt); num[x]+=num[tt]; } } }*/ //没卵用的初始化. void dp(int x) { v[x]=1; for(int i=head[x];i;i=a[i].next) { int tt=a[i].to; if(!v[tt]) { f[tt][1]=a[i].w; dp(tt); for(int j=m;j>=1;j--) for(int k=0;k<=j;k++) if((k!=j&&j!=1)||x==1) f[x][j]=max(f[x][j],f[tt][k]+f[x][j-k]); } } } int main() { scanf("%d%d",&n,&m); for(int i=1;i<n;i++) { int x,y,z; scanf("%d%d%d",&x,&y,&z); add(x,y,z); add(y,x,z); } add(0,1,0); pre(0); //memset(f,-1,sizeof(f)); dp(0); cout<<f[1][m]<<endl; }
原文地址:https://www.cnblogs.com/Kv-Stalin/p/9084756.html
时间: 2024-11-12 09:27:12