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; }