CF 1249F Maximum Weight Subset - 樹形DP

CF 1249F Maximum Weight Subset

題目鏈接:CF 1249F Maximum Weight Subset CF 1249F Maximum Weight Subset

算法標籤: 深搜樹形DP

題目描述:

給定一棵含 \(n\) 個節點的樹,每個節點含一個點權 \(a[i]\) 。現在請你選出一些節點,使得這些節點的權值和最大並且這些節點中任意兩個節點的距離都 \(>k\) 。並輸出這個最大的權值。

輸入第一行含兩個整數 \(n,k\) ,之後是 \(n\) 個整數 \(a[i]\) ,之後是 \(n-1\) 行,每行兩個整數,描述樹的每條邊。

題解:

樹形DP

(CF div3最後一題很喜歡DP????? 逃~)

感覺上挺神仙的一道題,這個考場上應該是真的想不到………………

也是研究了小半天官方題解搞明白了。

下面開始正題:


我們設這棵樹有根並且根是\(1\) ,之後來考慮以什麼狀態完成DP:

\(dp[pos][dep]\)表示在\(pos\)的子樹中符合條件的最大權值點集的權值和,並且保證這個最大權值點集中深度最小的點的深度\(\ge dep\)。(不太好理解…………)

之後的思路就是:我們需要計算 \(pos\) 的所有子節點的 \(dp[~][~]\),以此來更新 \(dp[pos][0\sim n]\)

考慮當前的深度是 \(dep\) ,就有以下的兩種想法:

  • 如果 \(dep == 0\),我們可以得到

    \(dp[pos][dep] = val[pos]+\sum_{to \in children(pos)}dp[to][max(0, k - dep - 1)]\)

  • 如果 \(dep!=0\),則可以得到 (\(to \in children(pos)~~~to \neq now\))

    \(dp[pos][dep]=max\{dp[pos][dep],dp[to][dep-1]+\sum_{now \in children(pos)}dp[now][max(dep-1,k-dep-1)]\}\)

之後我們要明確一個問題,在這其中我們包含着 \(k-dep-1\) 的計算,其實這裏的 \(k = k_{初始}+1\) ,原因就是這裏的計算保證的是每一個選擇到的點都要與當前點的距離 \(\gt k\),而不是 \(\ge k\)

上圖理解一下:

CF 1249F Maximum Weight Subset p1.png

扔到整棵樹裏邊:

CF 1249F Maximum Weight Subset p2.png

下面看代碼:

AC代碼

#include <bits/stdc++.h>

using namespace std;

const int N = 210;

vector <int> e[N];

int n, k, val[N];

int dp[N][N];

void add(int x, int y) {
    e[x].push_back(y);
    e[y].push_back(x);
}

void dfs(int pos, int pre) {
    dp[pos][0] = val[pos];
    for (int i = 0; i < (int)e[pos].size(); i ++ ) {
        int to = e[pos][i];
        if (to != pre) {
            dfs(to, pos);
        }
    }
    for (int dep = 0; dep < N; dep ++ ) {
        if (dep == 0) {
            for (int i = 0; i < (int)e[pos].size(); i ++ ) {
                int to = e[pos][i];
                if (to != pre) {
                    dp[pos][dep] += dp[to][max(0, k - dep - 1)];
                }
            }
        }
        else {
            for (int i = 0; i < (int)e[pos].size(); i ++ ) {
                int to = e[pos][i];
                if (to == pre) {
                    continue ;
                }
                int dis = dp[to][dep - 1];
                for (int i = 0; i < (int)e[pos].size(); i ++ ) {

                    int now = e[pos][i];
                    if (now == to || now == pre) {
                        continue ;
                    }
                    dis += dp[now][max(dep - 1, k - dep - 1)];
                }
                dp[pos][dep] = max(dp[pos][dep], dis);
            }
        }
    }
    for (int dep = N - 1; dep > 0; dep -- ) {
        dp[pos][dep - 1] = max(dp[pos][dep - 1], dp[pos][dep]);
    }
}

int main() {
    scanf("%d%d", &n, &k);
    k ++ ;
    for (int i = 1; i <= n; i ++ ) {
        scanf("%d", &val[i]);
    }
    for (int i = 1; i <= n - 1; i ++ ) {
        int a, b;
        scanf("%d%d", &a, &b);
        add(a, b);
    }
    dfs(1, 0);
    cout << dp[1][0] << endl;
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章