洛谷P1352—沒有上司的舞會(樹形DP)
阿新 • • 發佈:2021-07-14
樹形DP就是在樹的基礎上做動態規劃
樹形DP有兩個方向:
1.葉->根,回溯是從葉子結點往上更新
2.根->葉,往往是在從葉往根dfs一遍之後(相當於預處理),再重新往下獲取最後的答案。
題意
某大學有 n 個職員,編號為 1…n。
他們之間有從屬關係,也就是說他們的關係就像一棵以校長為根的樹,父結點就是子結點的直接上司。
現在有個週年慶宴會,宴會每邀請來一個職員都會增加一定的快樂指數 R[i] ,但是呢,如果某個職員的直接上司來參加舞會了,那麼這個職員就無論如何也不肯來參加舞會了。
所以,請你程式設計計算,邀請哪些職員可以使快樂指數最大,求最大的快樂指數。
思路
這道題是樹形DP中的典中典,從葉子往根回溯。
建樹:根據上司和員工的從屬關係建立一個有向樹
每個結點有一個價值,父節點和子節點不能同時選。
每個結點都是選和不選兩種選擇。
狀態轉移陣列:
dp[u][0]為u結點不去,獲得的子樹的最大價值
dp[u][1]為u結點去,獲得的子樹d餓最大價值
可以看出一共有三種情況和對應的狀態轉移方程:
1.上司去,員工不去dp[u][1] = r[u] + dp[v][0]
2.上司不去,員工去dp[u][0] = dp[v][1]
3.上司不去,員工不去dp[u][0] = dp[v][0]
第二種和第三種情況合併dp[u][0] = max{dp[v][1],dp[u][0]}
對根節點進行dfs,最終答案為max{dp[root][0],dp[root][1]}
作者:inss!w! 出處:https://www.cnblogs.com/Hfolsvh/ 版權宣告:本部落格所有文章除特別宣告外,均採用 BY-NC-SA 許可協議。轉載請註明出處!#include <iostream> #include<algorithm> #include<cmath> using namespace std; const int MAX_N = 6010; //最多結點數 int tot; //標記邊的序號 int head[MAX_N],nxt[MAX_N],ver[MAX_N]; //建樹要用到的陣列 int r[MAX_N],flag[MAX_N]; //r為價值,flag標記u是否有上司 int dp[MAX_N][2]; void addedge(int u,int v){ //根據鄰接表建樹的過程 ver[++tot] = v; //tot條邊指向的點為v nxt[tot] = head[u]; //nxt儲存以u為始點的下一條邊的序號 head[u] = tot; //head[u]儲存以u為始點的邊的序號 } void dfs(int u){ dp[u][0] = 0; //初始化u不去得到的dp dp[u][1] = r[u]; //初始化u去得到的dp for(int i = head[u]; i;i = nxt[i]){ //搜尋u的所有孩子 int v = ver[i]; dfs(v); dp[u][1] += dp[v][0]; dp[u][0] += max(dp[v][0],dp[v][1]); } } int main(){ int n; cin >> n; for(int i = 1;i <= n;i++) cin >> r[i]; int u,v; for(int i = 1;i <= n-1;i++){ cin >> v >> u; addedge(u,v); flag[v] = 1; //v有上司 } int root; for(int i = 1;i <= n;i++){ //尋找根節點 if(!flag[i]){ root = i; break; } } dfs(root); //從根節點開始dfs cout << max(dp[root][0],dp[root][1]) << endl; //根節點也有選和不選兩種選擇 return 0; }