hdoj1561The more, The Better(樹形dp,依賴揹包)
阿新 • • 發佈:2019-02-10
題意:ACboy很喜歡玩一種戰略遊戲,在一個地圖上,有N座城堡,每座城堡都有一定的寶物,在每次遊戲中ACboy允許攻克M個城堡並獲得裡面的寶物。但由於地理位置原因,有些城堡不能直接攻克,要攻克這些城堡必須先攻克其他某一個特定的城堡。你能幫ACboy算出要獲得儘量多的寶物應該攻克哪M個城堡嗎?
分析:
分類:樹形dp入門,依賴揹包
做這題前又看了一遍揹包9講,感覺太經典了,尤其是泛化揹包,簡直是精華。這題的關係就是裸地依賴揹包,用樹形dp解。
首先,限制條件是選擇m個物品,而每個物品最多選一次,跟0-1揹包的區別在於有依賴關係,那麼這層依賴關係我們可以藉助於一個樹來解決。藉助dfs,從根節點開始dfs,然後直到葉子節點,回朔的時候進行0-1揹包dp。
定義狀態:dp [ i ] [ j ] 表示在節點i,從以i為根節點的子樹下選擇j個城市的最大價值
初始化:dp [ i ] [ j ] =val [ i ](i節點的價值)(1 < = j < = m)
轉移方程 dp【father】【j】 = max (dp【father】【j】,dp【father】【k】+dp【child】【j-k】);由前面的dfs可見,我們是用子節點更新父節點,用j列舉父節點選擇的城市數,k列舉留給其他子節點選擇城市數,那麼就可以轉移了
注意:此題出給很多初始節點,也就是有很多森林,我們要設定一個超級root連線子樹的根節點即可。
程式碼:
#include <iostream> #include <vector> #include <cstring> #include <cstdio> #include <string> #include <algorithm> #include <vector> #define Del(a,b) memset(a,b,sizeof(a)) const int N = 150; using namespace std; int n,m; int dp[N][N],vis[N]; //dp[i][j]表示在節點i,從以i為根節點的子樹下選擇j個城市的最大價值 int cap[N],val[N]; vector<int> v[N]; void creat(int o) { for(int i=0; i<v[o].size(); i++) { int t=v[o][i]; if(vis[t] == 0 && v[t].size()>0) creat(t); for(int j = m ; j > 1 ; j--) //j>1表示此節點一定要取 0-1揹包 { for(int k=1; k<j; k++) //列舉給當前節點的其他子樹留多少可選擇的城市 dp[o][j]=max(dp[o][j],dp[o][k]+dp[t][j-k]); } } } int main(){ while(cin >> n >> m , n&&m){ m ++; // 加一個根節點 Del(dp,0); for(int i = 1 ; i <= n ; i ++){ int a , b; cin >> a >> b; v[a].push_back(i); for(int j = 1 ; j <= m ; j++) dp[i][j] = b; // 初始化時,每個節點,所有狀態都是拿自己一個 } creat(0); for(int i = 0 ; i <= n ; i ++) v[i].clear(); cout << dp[0][m] << endl; } return 0; }