题目大意
有一棵二叉的苹果树,(既然是树的话那就有n个节点和n-1个枝条),现在我们要剪枝,使得留下m个枝条,但是有些枝条上是有苹果的,求剪哪些枝条才能够保住最多的苹果。(虽然题目没说,但由于这是一棵苹果树,你只能剪叶子节点的枝条,所以是不能贪心的)
输入格式
第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 2021
题目分析
不能贪心,而且这是二叉树,二叉树,是最好写树形dp的数据结构了!
烦恼的是苹果是长在枝条上的,其实你可以把苹果挪到这个枝条连接的子节点上,因为如果减去这个枝条,就不会保留子节点。那么我们就从选枝条变成了选节点。并且由于一定要留根节点,所以m++.
如果用f[i][j]表示第i个节点的话,不难推导,f[i][j]=max(f[l[i]][k]+f[l[i]][j-k-1]+a[i]);用dfs来进行这个过程,每次dfs你需要的f[i][j]。
具体还是看代码吧。
代码
#include<iostream>
#include<algorithm>
#include<string>
#include<vector>
#include<cstdlib>
#include<cstdio>
#include<cstring>
#include<queue>
#include<climits>
using namespace std;
int n,m;
int l[101],r[101],a[101];
int bian[101][4],sum[101][4];
int f[101][101];
bool vis[101],vv[101][101];
void dfs(int now){//转化成二叉树
int i;
vis[now]=1;
for(i=1;i<=bian[now][0];i++)//寻找子节点
if(vis[bian[now][i]]==0){
if(l[now]==0){
l[now]=bian[now][i];
a[l[now]]=sum[now][i];//挪动苹果
dfs(bian[now][i]);
}
else{
r[now]=bian[now][i];a[r[now]]=sum[now][i];
dfs(bian[now][i]);
}
}
}
void find(int i,int j){
int k;
if(vv[i][j]==1)return;
vv[i][j]=1;//记忆化,如果已经搜出了f[i][j]就不用搜了
if(j==0||i==0)return;
for(k=0;k<=j-1;k++){
find(l[i],k);
find(r[i],j-k-1);//先弄出你需要的f
f[i][j]=max(f[i][j],f[l[i]][k]+f[r[i]][j-k-1]+a[i]);//dp方程
}
}
int main()
{
int i,j,x,y,apple;
scanf("%d%d",&n,&m);
for(i=1;i<=n-1;i++){
scanf("%d%d%d",&x,&y,&apple);
bian[x][0]++;bian[x][bian[x][0]]=y;sum[x][bian[x][0]]=apple;
bian[y][0]++;bian[y][bian[y][0]]=x;sum[y][bian[y][0]]=apple;
}
dfs(1);
m++;//要选择根节点1
find(1,m);
printf("%d",f[1][m]);
return 0;
}