hdu 1561
地址:http://acm.hdu.edu.cn/showproblem.php?pid=1561
题意:要攻克一些城堡,有些城堡可以直接攻克,有些必须要先攻克一个指定城堡才能攻克他。每个城堡里面有一些宝物,求攻克m个城堡的最大宝物数。
mark:该题用到一个很实用的技巧就是题目给定的是一片森林,那么我们为这些根节点建立一个共同的根节点,那么就变成一棵树了。
常规树形dp解法。dp[i][j]代表第i个节点为根节点的节点个数为j的最大价值。则dp[v][1] = w[v];(v是叶子节点)
dp[i][j] = max(dp[i][j], dp[i][k]+dp[son][j-k]);
这里用到一个小的剪枝,那就是dp[i][j]的j最大值是根据不断更新的情况定的,可以减少一些无用状态的转移。
代码:
#include <stdio.h> #include <string.h> #include <stdlib.h> const int N = 210; int n,m; int head[N],son[N]; int w[N]; int dp[N][N]; int max(int a, int b) {return a > b ? a : b;} int dfs(int fa) { int i,j,k; int tot = 1; dp[fa][1] = w[fa]; for(i = son[fa]; i != -1; i = head[i]) { tot += dfs(i); //剪枝,每次只用循环到当前能达到的最大值。 for(j = tot; j >= 1; j--) for(k = 1; k+j <= tot+1; k++) dp[fa][j+k] = max(dp[fa][j+k], dp[fa][j]+dp[i][k]); } return tot; } int main() { int i,j,k; while(scanf("%d%d", &n, &m), n+m) { memset(son, -1, sizeof(son)); w[0] = 0; for(i = 1; i <= n; i++) { scanf("%d%d", &j, w+i); head[i] = son[j]; son[j] = i; } memset(dp, 0, sizeof(dp)); dfs(0); printf("%d\n", dp[0][m+1]); } return 0; }