洛谷P2015—二叉苹果树(树形DP)
题意
有n个结点,树枝上有苹果,需要保留一些树枝,问最多能留下多少苹果?
输入输出
输入:第一行 2个整数 N 和 Q,分别表示表示树的结点数,和要保留的树枝数量。
接下来 N-1 行,每行 3 个整数,描述一根树枝的信息:前 2 个数是它连接的结点的编号,第 3 个数是这根树枝上苹果的数量。
输出:一个数,最多能留住的苹果的数量。
思路
状态转移数组
dp[i][j]为以i为根节点的子树保留j条树枝时获得的最大的苹果数量
最终的答案为dp[1][q]
状态转移方程
dp[u][j] = max(dp[u][j],dp[v][k] + dp[u][j-k-1] + edge[i]);
在u结点保留的j根树枝中,分k根树枝给它的子树,即dp[v][k],再加上子树到u那条边上的苹果树,u还剩下j-k+1条边
#include <iostream>
#include<algorithm>
#include<cmath>
using namespace std;
const int MAX_N = 110; //最多结点数
int tot; //标记边的序号
int head[MAX_N << 1],nxt[MAX_N << 1],ver[MAX_N << 1],edge[MAX_N << 1]; //建树要用到的数组
int dp[MAX_N][MAX_N];
int n,q;
void addedge(int u,int v,int w){ //根据邻接表建树的过程
ver[++tot] = v; //tot条边指向的点为v
nxt[tot] = head[u]; //nxt保存以u为始点的下一条边的序号
head[u] = tot; //head[u]保存以u为始点的边的序号
edge[tot] = w;
}
void dfs(int u,int fa){
dp[u][0] = 0;
for(int i = head[u];i;i = nxt[i]){
int v = ver[i];
if(v == fa) continue;
dfs(v,u);
for(int j = q; j >= 1;j--){ //枚举当前可以选的树枝数
for(int k = j - 1;k >= 0;k--){ //枚举子树可以选的树枝数
dp[u][j] = max(dp[u][j],dp[v][k] + dp[u][j-k-1] + edge[i]);
}
}
}
}
int main(){
cin >> n >> q;
int u,v,w;
for(int i = 0;i < n - 1;i++){
cin >> u >> v >> w;
addedge(u,v,w);
addedge(v,u,w);
}
dfs(1,-1);
cout << dp[1][q];
return 0;
}