hdu 1561 (樹形dp+依賴揹包)
阿新 • • 發佈:2019-02-04
ACboy很喜歡玩一種戰略遊戲,在一個地圖上,有N座城堡,每座城堡都有一定的寶物,在每次遊戲中ACboy允許攻克M個城堡並獲得裡面的寶物。但由於地理位置原因,有些城堡不能直接攻克,要攻克這些城堡必須先攻克其他某一個特定的城堡。你能幫ACboy算出要獲得儘量多的寶物應該攻克哪M個城堡嗎?
分析:
和依賴揹包有些類似,就是想要取一個值的時候要先取一個其他的值。 這個題目明顯可以先根據依賴關係建一棵樹,然後就是考慮怎麼對這棵樹進行dp了,所以是樹形dp。
這種題目也是第一次見到,可能有一個套路吧。dp[i][j] 就是在以i為根的子樹裡面取j個節點的最大值。最終結果就是dp[0][m],這棵樹的根節點為0.
//方程dp[n][j]=max(dp[n][j],dp[n][j-k]+dp[son[n][i]][k])
//表示在第i個n的子點中選k個點加上目前k號點選j-k個點與直接在k號點選j個點誰更優
#include <cstdio> #include <cstring> #include <cmath> #include <algorithm> #include <algorithm> #include <queue> #include <stack> #include <map> using namespace std ; #define mem(a) memset(a,0,sizeof(a)) #define inf 100000005 int const maxn = 205; int dp[maxn][maxn]; vector<int>son[maxn]; void dfs(int n,int left) { int len = son[n].size(); for(int i = 0 ; i < len ; i++) //節點n的所有兒子節點 { if(left>1)dfs(son[n][i],left-1); for(int j = left ; j >= 1 ; j--) { //一個類揹包的求解 for(int k = 1 ; k <= j ; k++) { dp[n][j+1] = max(dp[n][j+1] , dp[n][j+1-k]+dp[son[n][i]][k]); //方程dp[n][j]=max(dp[n][j],dp[n][j-k]+dp[son[n][i]][k]) //表示在第i個n的子點中選k個點加上目前k號點選j-k個點與直接在k號點選j個點誰更優 } } } } int main() { int n,m; while(scanf("%d%d",&n,&m)&&(m+n)) { mem(dp); for(int i = 0 ; i <= n ; i++) { son[i].clear(); } for(int i = 1 ; i <= n ; i++) { int a,b; scanf("%d%d",&a,&b); dp[i][1] = b ; //初始化,一個節點處要是隻取一個節點的話,肯定是本身 son[a].push_back(i) ; //要想i必須先a,所以i是a的兒子 } m++; //增加了一個節點0 dfs(0,m); printf("%d\n",dp[0][m]); } return 0 ; }