題目鏈接: hdu-1561
題目
ACboy很喜歡玩一種戰略游戲,在一個地圖上,有N座城堡,每座城堡都有一定的寶物,在每次游戲中ACboy允許攻克M個城堡並獲得裡面的寶物。但由於地理位置原因,有些城堡不能直接攻克,要攻克這些城堡必須先攻克其他某一個特定的城堡。你能幫ACboy算出要獲得盡量多的寶物應該攻克哪M個城堡嗎?
思路
簡單樹形背包dp,當作樹形背包的第一道入門題非常合適
題目給的是森林,那麼給增加一個"根節點", 指向森林中的每個樹的根節點
f(i, j)表示子樹i, 取j個城堡寶藏的時候的最大值
i的每個子節點表示的子樹可以看作是一組物品,在這個子樹上可以選擇取1,2,3...j-1個的城堡寶藏
那麼就是等價於對每個子節點做分組背包.
f(i, j) = max{ max{f(i, j-k)+f(v, k) | 1<=k<j } | v是i的子樹 }
因為增加了一個根節點,而且拿子節點之前必須先拿父節點,所以m值要增加1,用來拿根節點.
有個優化, 對於某個子樹,它取的個數不可能超過這個子樹的節點數,優化了可以到0MS
代碼
/**===================================================== * This is a solution for ACM/ICPC problem * * @source : hdu-1561 The more, The Better * @description : 樹形背包dp * @author : shuangde * @blog : blog.csdn.net/shuangde800 * @email : [email protected] * Copyright (C) 2013/08/20 11:27 All rights reserved. *======================================================*/ #include <iostream>#include <cstdio>#include <algorithm>#include <vector> #include <queue> #include <cmath> #include <cstring> #define MP make_pairusing namespace std; typedef pair<int, int >PII; typedef long long int64; const int INF = 0x3f3f3f3f; const double PI = acos(-1.0); const int MAXN = 210;vector<int>adj[MAXN]; int val[MAXN]; int tot[MAXN]; int f[MAXN][MAXN]; int n, m; int dfs(int u) { f[u][1] = val[u]; tot[u] = 1; for (int i = 0; i < adj[u].size(); ++i) { int v = adj[u][i]; tot[u] += dfs(v); } for (int i = 0; i < adj[u].size(); ++i) { int v = adj[u][i]; for (int s = tot[u]>m?m:tot[u]; s >= 1; --s) { for (int j = 1; j < s && j <= tot[v]; ++j) { f[u][s] = max(f[u][s], f[u][s-j] + f[v][j]); } } } return tot[u]; }int main(){ while (~scanf("%d %d", &n, &m) && n + m) { // init for (int i = 0; i <= n; ++i) adj[i].clear(); val[0] = 0; for (int i = 1; i <= n; ++i) { int a; scanf("%d %d", &a, &val[i]); if (a) { adj[a].push_back(i); } else { adj[0].push_back(i); } } memset(f, 0, sizeof(f)); ++m; dfs(0); printf("%d\n", f[0][m]); } return 0;}