Maximum Weight Subset
大致题意:
给定一棵含 n个节点的树,每个节点含一个点权 w[i]
要求选出一些节点,使得这些节点的权值和最大并且这些节点中任意两个节点的距离都 >k ,输出这个最大的权值
解题思路:
树形dp
状态表示:f[i][j] 表示以i为根节点,深度是j的满足条件的最大值
分析: f[i][j]有两种情况转移得来
一是取根节点这个点权,然后取以距离根节点>k的点为根节点的满足条件的最大权值和
用样例一来简单解释第一种情况:以1为根节点,k=1 ,我们做法是取根节点1,然后取以距离根节点>k的点为根节点的满足条件的最大权值和 也就是对于以2为根的子树我们取2(权值为2),对于以3为根的子树我们取3(权值为3),但是对于以5为根的子树来说,它的最大权值和是6,所以我们取6(权值为6)
二是 不取根节点这个点权,枚举深度j,取以距离根节点为j的点为根节点的满足条件的最大权值和+以根节点其它孩子距离根节点k-j+1距离的点为根节点的满足条件的最大权值和(可能有多个)
用样例二来简单解释第二种情况:以1为根节点,k=2 我们的做法是不取根节点,枚举深度,取以距离根节点为j的点为根节点的满足条件的最大权值和,当深度为1时,此时节点是5,那么以5为为根节点的满足条件的最大权值和是2+1,(节点5和节点6) ,然后加上以根节点其它孩子距离根节点k-j+1距离的点为根节点的满足条件的最大权值和(可能有多个),也就是节点2,那么以2为为根节点的满足条件的最大权值和是1,(节点2) ,所以最后权值和为2+1+1=4
转移方程:
情况一:
情况二:
补充:
剩下的就是代码细节问题,具体见代码注释
时间复杂度:O(n^3)
AC代码:
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 1; i <= (n); ++i)
#define debug(a) cout << #a << " = " << a << endl;
using namespace std;
typedef long long ll;
const int N = 210;
int n, k;
int w[N];
vector<int>e[N];
int f[N][N]; //表示以i为根节点,深度是j的满足条件的最大值
void dfs(int u, int fa) {
f[u][0] = w[u];
for (auto& v : e[u]) {
if (v == fa)continue;
dfs(v, u);
}
//第一种情况
for (auto& v : e[u]) { //深度是0
if (v == fa)continue;
f[u][0] += f[v][k];
}
//第二种情况
for (int i = 1; i < n; ++i) { //枚举深度i
for (auto& v1 : e[u]) { //其中一个孩子深度是i
if (v1 == fa)continue;
int tmp = f[v1][i - 1]; //其中一种满足条件的最大值
for (auto& v2 : e[u]) { //除v1外的其他孩子
if (v2 == v1 || v2 == fa)continue;
tmp += f[v2][max(i - 1, k - i)]; //保证任意两点距离大于k
}
f[u][i] = max(f[u][i], tmp); //更新根节点是u,深度是i的满足条件的最大值
}
}
//更新f[u][i]
for (int i = n - 1; i > 0; --i)
f[u][i - 1] = max(f[u][i - 1], f[u][i]);
}
int main(void)
{
ios::sync_with_stdio(0); cin.tie(0);
cin >> n >> k;
for (int i = 1; i <= n; ++i)cin >> w[i];
for (int i = 1; i < n; ++i) {
int a, b; cin >> a >> b;
e[a].push_back(b); e[b].push_back(a);
}
dfs(1, -1); //以1为根
cout << f[1][0] << endl;
return 0;
}