hiho_1055_刷油漆

题目大意

    一棵树,每个节点都有相应的value值。从根开始选择M个节点相互连通,使得这些节点的value值之和最大。 
题目链接:[刷油漆][1]

题目分析

    典型的树形dp,dp[i][j] 表示以节点i为根的子树中选择j个节点(从i节点出发)相互连通,所能达到的节点value之和的最大值。可以很容易知道, 
dp[root][k] = max{dp[ch1][k1] + dp[ch2][k2] + .. dp[cht][kt]}. 
即将k-1个节点分配给 根节点root的t个子节点,其中 k1 + k2 + .. + kt = k -1. 
    看起来要枚举 ch1, ch2...cht 所能分配到的节点数k1, k2...kt 的所有情况,但这可以利用有限背包问题类似的做法, 在进行dfs方式求解时,每求解完根节点root的一个子节点 ch,就可以类似背包问题来进行 
状态优化。

//dp[node][i]表示以node为根的子树中选择i个节点,且节点相互连通,所能达到的value和的最大值。
//注意i要从大到小进行枚举,且j一定小于i,不能等于i,因为node节点必须被占用
for (int i = M; i >= 2; i--) {
    for (int j = 1; j < i; j++) {
        dp[node][i] = dp[node][i] > dp[node][i - j] + dp[v][j] ? dp[node][i] : dp[node][i - j] + dp[v][j];
        }
}

 实现

#include<iostream>
#include<stdio.h>
#include<string.h>
using namespace std;
struct Edge {
	int to;
	int next;
};

int gNodeValue[105];
int dp[105][105];
int gHead[105];
bool gVisited[105];
int gValue[105];
Edge gEdges[220];
int gEdgeIndex;

void InsertEdge(int u, int v) {
	int e = gEdgeIndex++;
	gEdges[e].to = v;
	gEdges[e].next = gHead[u];
	gHead[u] = e;

	e = gEdgeIndex++;
	gEdges[e].to = u;
	gEdges[e].next = gHead[v];
	gHead[v] = e;
}
int min(int a, int b) {
	return a < b ? a : b;
}
void Dfs(int node, int m) {
	gVisited[node] = true;
	int e = gHead[node];
	dp[node][1] = gValue[node];
	for (e; e != -1; e = gEdges[e].next) {
		int v = gEdges[e].to;
		if (!gVisited[v]) {
			Dfs(v, m);
			for (int i = m; i >= 2; i--) {
				for (int j = 1; j < i; j++) {
					dp[node][i] = dp[node][i] > dp[node][i - j] + dp[v][j] ? dp[node][i] : dp[node][i - j] + dp[v][j];
				}
			}
			
		}
	}	
}

void Init() {
	memset(gValue, 0, sizeof(gValue));
	memset(dp, 0, sizeof(dp));
	memset(gValue, false, sizeof(gVisited));
	memset(gEdges, -1, sizeof(gEdges));
	gEdgeIndex = 0;
	memset(gSuccessorNum, 0, sizeof(gSuccessorNum));
	memset(gHead, -1, sizeof(gHead));
}

int main() {
	int n, m;
	Init();
	scanf("%d %d", &n, &m);
	for (int i = 1; i <= n; i++) {
		scanf("%d", &gValue[i]);
	}
	int u, v;
	for (int i = 1; i < n; i++) {
		scanf("%d %d", &u, &v);
		InsertEdge(u, v);
	}
	Dfs(1, m);
	printf("%d\n", dp[1][m]);
	return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值