2 5 \ / 3 4 \ / 1As you may know it's not convenient to pick an apples from a tree when there are too much of branches. That's why some of them should be removed from a tree. But you are interested in removing branches in the way of minimal loss of apples. So your are given amounts of apples on a branches and amount of branches that should be preserved. Your task is to determine how many apples can remain on a tree after removing of excessive branches.
5 2 1 3 1 1 4 10 2 3 20 3 5 20
21
剛開始學樹狀dp,歡迎來噴。
有一顆蘋果樹,它的樹枝符合完全二叉樹。每個樹枝上有一些蘋果。節點標號為1~N(1為根節點)。現在因為樹枝太多了,需要保留q根樹枝。問最多能保留多少蘋果。
題目給出的數據並沒有按照跟節點到子節點的順序給出,如何建樹便一直困擾了我好久(戰五渣-。-)。 最終借助結構體存放兩個兒子的下標比較優美地建出來了。
接下來講轉移。顯然我們不光要考慮節點,還要考慮以該節點為根的樹選擇了多少條邊。
dp[t][k] : 已 t 為根的子樹保留k條邊(樹枝)最多能保留多少條蘋果。
由此我們可以發現如果在某個節點保留了k條邊,有以下兩種情況。
1:只選擇一棵子樹。那麼dp[t][k] = max(dp[t.l][k-1] + w[t][t.l], dp[t.r][k-1] + w[t][t.r]) //意思是在該子樹(左或右)選擇k-1條邊,並加上根節點到該子樹的邊。
2:選擇兩顆子樹。那麼就存在兩條固定的邊(根節點到其左右孩子的邊),dp[t][k] = w[t][t.l] +w[t][t.r] + max(dp[t.l][j] + dp[t.r][k - j - 2]) /*(j : 0 ~ K-2)*/ 。
即左子樹和右子樹總共選擇k-2條邊。
由此,輸出dp[1][q]就好。
#include#include #include #include using namespace std; struct Node { int l, r; }T[105]; int n, q; int dp[105][105], map[105][105]; void buildTree(int t) {//建樹 int flag = 0; for (int i=0; i<=n; i++) { if (map[t][i] && !T[i].l && !T[t].r) {//!T[i].l不為父節點 if (!T[t].l) T[t].l = i; else T[t].r = i; flag = 1; } } if (flag) { buildTree(T[t].l); buildTree(T[t].r); } } void Tdp (int s) { if (T[s].l == 0) return ; Tdp(T[s].l); Tdp(T[s].r); for (int i=1; i<=q; i++) {//轉移 dp[s][i] = max(dp[T[s].l][i-1] + map[s][T[s].l], dp[T[s].r][i-1] + map[s][T[s].r]); for (int j=0; j > n >> q ; int a, b, c; for (int i=1; i > a >> b >> c; map[a][b] = map[b][a] = c; } buildTree(1); Tdp(1); cout << dp[1][q] <