题目分析:
对于这道题目,可以用分组背包的思想来处理,分组背包问题的基本思路是,在每个组里只能选取一个物品,从而让背包在容量限制下达到最大价值。在这个代码场景中,以子节点 v 为根的子树的所有可达路径和可以看成一个物品组,每一个可达路径和就是组内的一个物品。
解题方法:
定义 dp[i][j]
表示以节点 i 为根的子树中,是否存在路径和为 j 的路径,在递归函数中,首先对叶子节点进行初始化dp[u][w[u]] = true
这是显然的,然后开始做分组背包的板子,只要dp[u][j-k]
和dp[v][k]
都存在,那就能推出dp[u][j]
存在。
最后从大到小枚举根节点的路径和,存在的第一个即为答案。
时间复杂度 O(n2)
参考代码
#include <bits/stdc++.h>
using namespace std;
const int N = 1010;
vector<int> g[N];
int w[N], n;
bool dp[N][N]; // dp[i][j] 表示以节点 i 为根的子树中,是否存在路径和为 j 的路径
void dfs(int u, int fa) {
dp[u][0] = true;
if (g[u].size() == 1 && fa != 0) {
dp[u][w[u]] = true;
return;
}
for (int v : g[u]) {
if (v == fa) continue;
dfs(v, u);
for (int j = w[u]; j >= 0; j--)
for (int k = 0; k <= j; k++)
if (dp[u][j - k] && dp[v][k])
dp[u][j] = true;
}
}
int main() {
ios::sync_with_stdio(false), cin.tie(nullptr);
cin >> n;
for (int i = 1; i <= n; i++) cin >> w[i];
for (int i = 1; i < n; i++) {
int x, y;
cin >> x >> y;
g[x].push_back(y);
g[y].push_back(x);
}
dfs(1, 0);
int ans = 0;
for (int i = w[1]; i >= 0; i--) {
if (dp[1][i]) {
ans = i;
break;
}
}
cout << ans << "\n";
return 0;
}