Maximum Weight Subset (树形dp图文解释)

Maximum Weight Subset

题目链接

大致题意:

给定一棵含 n个节点的树,每个节点含一个点权 w[i]
要求选出一些节点,使得这些节点的权值和最大并且这些节点中任意两个节点的距离都 >k ,输出这个最大的权值


解题思路:

树形dp

状态表示:f[i][j] 表示以i为根节点,深度是j的满足条件的最大值

分析: f[i][j]有两种情况转移得来

是取根节点这个点权,然后取以距离根节点>k的点为根节点的满足条件的最大权值和

img

用样例一来简单解释第一种情况:以1为根节点,k=1 ,我们做法是取根节点1,然后取以距离根节点>k的点为根节点的满足条件的最大权值和 也就是对于以2为根的子树我们取2(权值为2),对于以3为根的子树我们取3(权值为3),但是对于以5为根的子树来说,它的最大权值和是6,所以我们取6(权值为6)

是 不取根节点这个点权,枚举深度j,取以距离根节点为j的点为根节点的满足条件的最大权值和+以根节点其它孩子距离根节点k-j+1距离的点为根节点的满足条件的最大权值和(可能有多个)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Td7E5lKs-1624534444523)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210624192829894.png)]

用样例二来简单解释第二种情况:以1为根节点,k=2 我们的做法是不取根节点,枚举深度,取以距离根节点为j的点为根节点的满足条件的最大权值和,当深度为1时,此时节点是5,那么以5为为根节点的满足条件的最大权值和是2+1,(节点5和节点6) ,然后加上以根节点其它孩子距离根节点k-j+1距离的点为根节点的满足条件的最大权值和(可能有多个),也就是节点2,那么以2为为根节点的满足条件的最大权值和是1,(节点2) ,所以最后权值和为2+1+1=4

转移方程:

情况一:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HcU2ceV4-1624534444524)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210624193256373.png)]

情况二:[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YOgiiXei-1624534444527)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210624193320692.png)]

补充:

剩下的就是代码细节问题,具体见代码注释

时间复杂度: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;
}

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值