题目描述
有一棵苹果树,如果树枝有分叉,一定是分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个。
输出格式
一个数,最多能留住的苹果的数量。
题解:
非常入门的树上背包问题,注意跑01背包的时候一定一定要从大到小遍历容量!!!
#include<bits/stdc++.h> using namespace std; const int maxn=1010; typedef long long ll; int dp[maxn][maxn];//dp(i,j)表示在以i为根的子树里选择j个枝条的最优解 int n,q; struct node { int u,v,w,nxt; }edge[maxn]; int head[maxn]; int tot; int size[maxn];//以i为根的子树的节点数量,枝条为子树节点数-1 void addedge (int u,int v,int w) { edge[tot].u=u; edge[tot].v=v; edge[tot].w=w; edge[tot].nxt=head[u]; head[u]=tot++; edge[tot].u=v; edge[tot].v=u; edge[tot].w=w; edge[tot].nxt=head[v]; head[v]=tot++; } void dfs1 (int u,int pre) { size[u]=1; for (int i=head[u];i!=-1;i=edge[i].nxt) { int v=edge[i].v; if (v==pre) continue; dfs1(v,u); size[u]+=size[v]; } } void dfs2 (int u,int pre) { for (int i=head[u];i!=-1;i=edge[i].nxt) { int v=edge[i].v; if (v==pre) continue; dfs2(v,u); for (int j=size[u];j>=0;j--) { for (int k=size[v]-1;k>=0;k--) { if (j>=k+1) dp[u][j]=max(dp[u][j],dp[u][j-k-1]+dp[v][k]+edge[i].w); } } } } int main () { scanf("%d%d",&n,&q); for (int i=1;i<=n;i++) head[i]=-1; tot=0; for (int i=1;i<n;i++) { int x,y,w; scanf("%d%d%d",&x,&y,&w); addedge(x,y,w); } dfs1(1,0); dfs2(1,0); int ans=0; for (int i=0;i<=min(q,size[1]);i++) ans=max(ans,dp[1][i]); printf("%d\n",ans); }