洛谷 P2015 二叉蘋果樹(codevs5565) 樹形dp入門


dp這一方面的題我都不是很會,所以來練(xue)習(xi),大概把這題弄懂了。

樹形dp就是在原本線性上dp改成了在 '樹' 這個數據結構上dp。

一般來說,樹形dp利用dfs在回溯時進行更新,使用兒子節點對父親節點進行更新。

樹形dp很多題需要在二叉樹上進行。

進入正題。

點我看題

這個圖是洛谷題面里奇奇怪怪的東西,格式弄好就這樣。

題意:有一棵已知根(1)的二叉樹,每條邊都有一個權值,現在可以保留 q 條邊,問在這樣的前提下,以 1 為根 的樹最多能有多少權值和。

題意可以畫個圖來解釋

這個就是樣例的圖,假設我萌只保留 1-->3 這條邊,辣么我萌得到的權值是 1-->3 這條邊的權值。

         假設我萌只斷掉 1-->3 這條邊,辣么可以得到的權值只有1-->4這條邊,因為如果1-->3沒了,2,5節點無法連通到1,

                3-->2   3-->5 的邊也就不是以1為根的樹里的了。

 思路:這題看到了二叉樹,於是可以往dp方向思考一下,發現是可行的。

           首先可以把所以邊的權值下移到節點上,這樣我萌列出轉移方程。

           設f[i,j]表示以 i 為根的子樹中,保留了 j  個節點得到的最大權值。

     設 i 的左兒子為son[i,1] 右兒子為son[i,2] 設權值下移后 x 節點的權值為cost[x]

   則對於 某個節點  x 來說,有三種選擇,一是選了左兒子這個點,不選右兒子。

                                                                           二是選了右兒子這個點,不選左兒子。

                      三是既選左兒子又選右兒子。

辣么分別列出轉移方程: ① f[i,j]=max(f[son[i,1],j-1]+cost[son[i,1]])  (如果選了son[i,1]則把該權值加上,因為枚舉的 j 表示的是保留 個節點,所以要保留son[i,1]的情況下,就要找他的前驅狀態 j-1 )

            ② f[i,j]=max(f[son[i,2],j-1]+cost[son[i,2]]) (這個和①是類似的,只是將左兒子改為右兒子)

            ③ f[i,j]=max(f[son[i,1],k]+f[son[i,2],j-2-k]+cost[son[i,1]+cost[son[i,2]) (這個看起來就要復雜得多,我萌畫圖看看)

 

這樣的話應該會很清晰了,辣么腫么去跑這個dp捏。

顯然我萌要先做一個預處理,用遞歸先把 cost[i] son[i,1] son[i,2] 預處理出來。

然后在用一個dfs遞歸進行dp。 對於 ①②兩種情況可以在遍歷邊的時候直接進行更新,但是對於③情況要在邊遍歷完后進行。

為什么? 由於遞歸的順序。比如樣例這個圖,他的順序是這樣的   1-->3-->2 好這里可以對2節點的f[2,j]進行更新了

                  然后 1-->3-->2-->3(回溯同時用2節點的信息進行①情況的更新)-->5-->3(此時3的邊都遍歷完了,先是用5節點的信息進行②情況的更新,然后再使用 2 和 5 的節點信息一起對3進行③情況更新)-->1(類似,用3對1進行①情況的更新) -->4-->1(類似,用4對1進行②情況的更新,用3 4對1進行③情況更新)

這樣就應該理解為什么要先把邊遍歷完才更新③情況了,因為只有這樣,要更新的節點的左右兒子的信息才是都已知的,這樣才能更新,滿足了dp需要利用前驅信息。

 1 type
2 node=record
3 y,z:longint;
4 next:longint;
5 end;
6 var num,father,first,cost:array[0..150]of longint;
7 son:Array[0..150,0..2]of longint;
8 i:longint;
9 n,q:longint;
10 x,y,z:longint;
11 tot:longint;
12 e:Array[0..250]of node;
13 f:array[0..150,0..150]of longint;
14 function max(a,b:longint):longint;
15 begin
16 if a>b then exit(a) else exit(b);
17 end;
18 procedure adde(x,y,z:longint);
19 begin
20 e[tot].next:=first[x];
21 e[tot].y:=y;
22 e[tot].z:=z;
23 first[x]:=tot;
24 inc(tot);
25 end;
26 procedure buildtree(x:longint);
27 var i,y:longint;
28 begin
29 i:=first[x];
30 while i<>-1 do
31 begin
32 y:=e[i].y;
33 if father[y]=0 then
34 begin
35 father[y]:=x;
36 cost[y]:=e[i].z;
37 inc(num[x]);
38 son[x,num[x]]:=y;
39 buildtree(y);
40 end;
41 i:=e[i].next;
42 end;
43 end;
44 procedure dfs(x:longint);
45 var i,j:longint;
46 l,r,y:longint;
47 begin
48 for i:=1 to num[x] do
49 begin
50 y:=son[x,i];
51 dfs(y);
52 for j:=1 to q do
53 f[x,j]:=max(f[x,j],f[y,j-1]+cost[y]);
54 end;
55 l:=son[x,1];
56 r:=son[x,2];
57 for i:=1 to q do
58 for j:=0 to i-2 do
59 f[x,i]:=max(f[x,i],f[l,j]+f[r,i-2-j]+cost[l]+cost[r]);
60 end;
61 begin
62 read(n,q);
63 for i:=1 to n do
64 first[i]:=-1;
65 for i:=1 to n-1 do
66 begin
67 read(x,y,z);
68 adde(x,y,z);
69 adde(y,x,z);
70 end;
71 father[1]:=1;
72 buildtree(1);
73 dfs(1);
74 writeln(f[1,q]);
75 end.
樹形dp

這題的話,我理解了挺久的,然后理解后直接就敲了一個代碼,然后一次過了,所以理解有時候會讓代碼更快實現。

版權聲明:要轉載請在評論區留言=v=


注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
粤ICP备14056181号  © 2014-2020 ITdaan.com