https://www.luogu.org/problemnew/show/P2015
二叉苹果树
时间限制: 1 Sec 内存限制: 128 MB
题目描述
有一棵苹果树,如果树枝有分叉,一定是分 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[i][j]表示子树i,保留j个节点的最大权值
每条边的权值,可以看作是儿子的权值,那么就可以对所有的子树做分组背包,即每个子树可以选则1,2..j-1条边分配给它
#include<bits/stdc++.h>
using namespace std;
const int maxn=150;
int dp[maxn][maxn];
vector< pair<int,int> >adj[maxn];//存储无向图
#define inf 0x3f3f3f3f
int tot[maxn];//tot[i]表示与节点i直接相邻的节点个数
int dfs(int u,int fa)//u的父节点为fa
{
tot[u]=1;
for(int i=0;i<adj[u].size();i++)
{
int v=adj[u][i].first;
if(v==fa) continue;
tot[u]+=dfs(v,u);
}
for(int i=0;i<adj[u].size();i++)
{
int v=adj[u][i].first;//与u直接相连节点
int w=adj[u][i].second;//这条边的权值
if(v==fa) continue;
for(int j=tot[u];j>1;j--)
{
for(int k=1;(k<j)&&(k<=tot[v]);k++)
{
dp[u][j]=max(dp[u][j],dp[u][j-k]+dp[v][k]+w);
}
}
}
return tot[u];
}
int main()
{
int n,p;
while(~scanf("%d%d",&n,&p)){
for(int i=0;i<maxn;i++)
adj[i].clear();
int u,v,w;
for(int i=1;i<n;i++)
{
scanf("%d%d%d",&u,&v,&w);
adj[u].push_back(make_pair(v,w));
adj[v].push_back(make_pair(u,w));
}
memset(dp,0,sizeof(dp));
dfs(1,-1);//从任意一点开始搜索
printf("%d\n",dp[1][p+1]);
}
return 0;
}