题目描述
有一棵苹果树,如果树枝有分叉,一定是分二叉(就是说没有只有一个儿子的结点)
这棵树共有 NN 个结点(叶子点或者树枝分叉点),编号为 1 \sim N1∼N,树根编号一定是 11。
我们用一根树枝两端连接的结点的编号来描述一根树枝的位置。下面是一颗有 44 个树枝的树:
2 5
\ /
3 4
\ /
1
现在这颗树枝条太多了,需要剪枝。但是一些树枝上长有苹果。
给定需要保留的树枝数量,求出最多能留住多少苹果。
输入格式
第一行 2个整数 N 和 Q,分别表示表示树的结点数,和要保留的树枝数量。
接下来 N-1行,每行 3 个整数,描述一根树枝的信息:前 2 个数是它连接的结点的编号,第 3 个数是这根树枝上苹果的数量
输出格式
一个数,最多能留住的苹果的数量。
树状dp模板题
首先这是一个树,所以我们可以用链制前向星存下来它的起点和终点
Q:那么苹果数怎么存呢?
A:直接存到它的枝子上了
也就是等于一般的前向星中的距离w
void add(int u,int v,int w)
{
edge[cnt].v=v;
edge[cnt].w=w;
edge[cnt].nxt=head[u];
head[u]=cnt++;
}
int main()
{
for(int i=1;i<n;i++)
{
int u,v,w;
cin>>u>>v>>w;
add(u,v,w);
add(v,u,w);
}
}
接下来就是套路
既然是树状dp
那么一个二维数组是必要的
开一个f[u][j]
u表示节点编号,j表示当前保留的树枝条数
定义一个k表示u链接的v节点保留的树枝
那么v节点的在保留k树枝时最大苹果数可以表示为f[v][k]
除了这个v节点剩下的树枝数就为j-k
注意 因为 u肯定与v有一条边
所以剩下的最大的苹果数就可以表示为f[u][j-k-1]
不要忘了加上这条枝子的苹果数w;
于是得到如下的dp方程
for(int i=head[u];i!=-1;i=edge[i].nxt)
{
int v=edge[i].v;
if(v==father) continue;
dfs(v,u);
for(int j=q;j!=0;j--)
{
for(int k=0;k<j;k++)
{
f[u][j]=max(f[u][j],f[u][j-k-1]+f[v][k]+edge[i].w);
}
}
}
剩下的就是输出了
若以1为根节点输出f[1][q]便是
完整代码
#include<bits/stdc++.h>
using namespace std;
const int M=1010;
struct enode{
int v,nxt,w;
}edge[M];
int head[M];
int cnt;
void add(int u,int v,int w)
{
edge[cnt].v=v;
edge[cnt].w=w;
edge[cnt].nxt=head[u];
head[u]=cnt++;
}
int n,q;
int sz[M],f[M][M];
void dfs(int u,int father)
{
for(int i=head[u];i!=-1;i=edge[i].nxt)
{
int v=edge[i].v;
if(v==father) continue;
dfs(v,u);
for(int j=q;j!=0;j--)
{
for(int k=0;k<j;k++)
{
f[u][j]=max(f[u][j],f[u][j-k-1]+f[v][k]+edge[i].w);
}
}
}
}
int main()
{
memset(head,-1,sizeof head);
cin>>n>>q;
for(int i=1;i<n;i++)
{
int u,v,w;
cin>>u>>v>>w;
add(u,v,w);
add(v,u,w);
}
dfs(1,0);
cout<<f[1][q];
return 0;
}
封面是我的洛谷头像,
磷叶石好脆弱我好推