洛谷题单指南-树与图上的动态规划-P2015 二叉苹果树
原题链接:https://www.luogu.com.cn/problem/P2015
题意解读:一棵树,边有权值,求从树中选择不超过q条边的最大权值和。
解题思路:
一、三维解法:
利用分组背包的思想,将一个节点的所有子树看做一组物品,枚举背包体积,再枚举某一组中所有的物品
1、状态表示
设f[i][j][k]表示以i为根的子树中在前j个子树中选总树枝数量不超过k时的最大苹果数
2、状态转移
设根节点u,对于u的每一个子节点v,有递推关系
枚举u的所有子树v (对应分组背包的一个分组)
枚举从树中可以选的最大边数k:0~q (对应背包的体积)
枚举可以从子树v中选择的边数s:0~k-1,最多是k-1因为u->v的边要单独算 (对应一组中的一种物品)
f[u][j][k] = f[u][j - 1][k]; //不选v这棵子树
for(int s = 0; s < k; s++) //选v这棵子树
{
f[u][j][k] = max(f[u][j][k], f[v][cnt[v]][s] + f[u][j - 1][k - s - 1] + w[i]);
}
3、初始化:f[x][0][x] = 0
4、结果:f[1][cnt[1]][q]
100分代码:
#include <bits/stdc++.h>
using namespace std;
const int N = 105, M = 205;
int head[N], to[M], w[M], nxt[M], idx;
int cnt[N]; //cnt[i]表示i有多少个子节点
int f[N][N][N]; //f[i][j][k]表示以i为根的子树中在前j个子树中选总树枝数量不超过k时的最大苹果数
int n, q;
void add(int a, int b, int c)
{
to[++idx] = b;
w[idx] = c;
nxt[idx] = head[a];
head[a] = idx;
}
void dfs(int u, int p)
{
int j = 0;
for(int i = head[u]; i; i = nxt[i])
{
int v = to[i];
if(v == p) continue;
dfs(v, u);
j++; //子节点数量加1
cnt[u]++; //记录u的子节点数量
for(int k = 0; k <= q; k++)
{
f[u][j][k] = f[u][j - 1][k]; //不选v这棵子树
for(int s = 0; s < k; s++) //选v这棵子树
{
f[u][j][k] = max(f[u][j][k], f[v][cnt[v]][s] + f[u][j - 1][k - s - 1] + w[i]);
}
}
}
}
int main()
{
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][cnt[1]][q];
return 0;
}
二、二维解法:
1、状态表示
设f[i][j]表示以i为根的子树中选总树枝数量不超过j时的最大苹果数
2、状态转移
设根节点u,对于u的每一个子节点v,有递推关系
枚举u的所有子树v (对应分组背包的一个分组)
枚举从树中可以选的最大边数j:q~0 (对应背包的体积,倒着遍历因为实际省掉了组这一维)
枚举可以从子树v中选择的边数k:0~j-1,最多是j-1因为u->v的边要单独算 (对应一组中的一种物品)
f[u][j] = max(f[u][j], f[v][k] + f[u][j - k - 1] + w[u->v]);
3、初始化:f[1][0] = 0
4、结果:f[1][q]
100分代码:
#include <bits/stdc++.h>
using namespace std;
const int N = 105, M = 205;
int head[N], to[M], w[M], nxt[M], idx;
int f[N][N]; //f[i][j]表示以i为根的子树中选总树枝数量不超过j时的最大苹果数
int n, q;
void add(int a, int b, int c)
{
to[++idx] = b;
w[idx] = c;
nxt[idx] = head[a];
head[a] = idx;
}
void dfs(int u, int p)
{
for(int i = head[u]; i; i = nxt[i])
{
int v = to[i];
if(v == p) 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[v][k] + f[u][j - k - 1] + w[i]);
}
}
}
}
int main()
{
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;
}