1. 程式人生 > 實用技巧 >hdu1520——樹形DP入門及幾道簡單題

hdu1520——樹形DP入門及幾道簡單題

樹形dp 

  樹形DP是指在“樹”這種資料結構上進行的DP,一般來說題目會暗示你去求一個最大值或最小值(比如最小代價,最大收益之類的)。而且一般來講這種問題的規模比較大,沒辦法列舉,貪心也不能得到最優解,所以要用到動規。

  而且,樹實在是太適合做動規了......因為樹本身就具有“子結構”的性質(子樹),所以在寫狀態轉移方程的時候比線性的dp更加直觀。(但是更難寫

  一般來說都要用到dfs。(大概

來幾個入門題幫助理解下

 hdu1520——Anniversary Party

  題意大概是給定一棵樹,樹上的每個節點都有對應的權值,要求不能同時選一個節點和他的父親節點,求可以取到的最大權值。

  顯然每個節點對應的狀態只有選和不選兩種,所以我們可以定義兩個狀態:

  • dp[i][0]為不選當前節點時的最優解
  • dp[i][1]為選擇當前節點時的最優解

  同時有兩個狀態轉移方程:

  1. 不選擇當前節點,子節點可選可不選 : dp[i][0]+=max(dp[son][1],dp[son][0])
  2. 選擇當前節點,子節點不能選: dp[i][1]+=dp[son][0]

  本題可以使用stl的vector建立關係樹。先找到一個根節點,然後向下dfs,在回溯時進行dp。下面是ac程式碼:

#include <bits/stdc++.h>
using
namespace std; const int N = 6005; int val[N], dp[N][2], fa[N], n; vector<int> tree[N]; void dfs(int u) { dp[u][0] = 0; dp[u][1] = val[u]; for (int i = 0; i < tree[u].size(); i++) { int son = tree[u][i]; dfs(son); dp[u][0] += max(dp[son][1], dp[son][0]); dp[u][
1] += dp[son][0]; } } int main() { while (~scanf("%d", &n)) { for (int i = 1; i <= n; i++) { scanf("%d", &val[i]); tree[i].clear(); fa[i] = -1; } while (1) { int a, b; scanf("%d%d", &a, &b); if (a == 0 && b == 0) break; fa[a] = b; tree[b].push_back(a); } int t = 1; while (fa[t] != -1) t = fa[t]; dfs(t); printf("%d\n", max(dp[t][1], dp[t][0])); } return 0; }