DP - 樹形DP - 二叉蘋果樹

DP - 樹形DP - 二叉蘋果樹

有一棵二叉蘋果樹,如果樹枝有分叉,一定是分兩叉,即沒有隻有一個兒子的節點。

這棵樹共 N 個節點,編號爲 1 至 N,樹根編號一定爲 1。

我們用一根樹枝兩端連接的節點編號描述一根樹枝的位置。

一棵蘋果樹的樹枝太多了,需要剪枝。但是一些樹枝上長有蘋果,給定需要保留的樹枝數量,求最多能留住多少蘋果。

這裏的保留是指最終與1號點連通。

輸入格式
第一行包含兩個整數 N 和 Q,分別表示樹的節點數以及要保留的樹枝數量。

接下來 N−1 行描述樹枝信息,每行三個整數,前兩個是它連接的節點的編號,第三個數是這根樹枝上蘋果數量。

輸出格式
輸出僅一行,表示最多能留住的蘋果的數量。

數據範圍
1≤Q<N≤100.
N≠1,
每根樹枝上蘋果不超過 30000 個。

輸入樣例:
5 2
1 3 1
1 4 10
2 3 20
3 5 20
輸出樣例:
21

分析:

11容易看出類似於一個樹上分組揹包問題。\\本題需要注意的是,1號節點必須保留,也就是必須保留一條與1號節點直接相連的邊。

1我們可以把每條邊的權值視作這條邊上孩子節點的權值,每個節點的體積視爲1,

12Q1使於是本題就是在以1號節點爲根的樹的2棵子樹中選擇Q-1個節點,使得收益最大。

1但是選擇每個節點都要保證該點與1號點是連通的。

1那麼從1號點向下選擇時,選擇每個節點都要先選擇其父節點。

f[i,j]ij狀態表示f[i,j]:表示以i爲根的樹,保留j條邊的最大收益。

狀態計算:
在這裏插入圖片描述
說明:
iijs1s1s1j1對節點i而言,將其兩個孩子節點視作兩個物品組,在兩個物品組中選擇物品。\\以i爲根的子樹中選擇j個物品,若在物品組s1中選,就要先選擇s1,那麼就在s1的孩子中選擇j-1個。

代碼:

#include<iostream>
#include<cstring>
#include<algorithm>

using namespace std;

const int N=110,M=2*N;

int n,m;
int e[M],ne[M],w[M],h[N],idx;
int f[N][N];    //f[i][j]:從以i爲根的子樹種,選j條邊的最大價值

void add(int a,int b,int c)
{
    e[idx]=b,w[idx]=c,ne[idx]=h[a],h[a]=idx++;    
}

int dfs(int u,int fa)
{
    for(int i=h[u];~i;i=ne[i])
    {
        int v=e[i];
        if(v==fa) continue;
        dfs(v,u);
        for(int j=m;j;j--)
            for(int k=0;k<j;k++)
                f[u][j]=max(f[u][j],f[u][j-k-1]+f[v][k]+w[i]);
    }
}

int main()
{
    memset(h,-1,sizeof h);
    
    cin>>n>>m;
    for(int i=0;i<n-1;i++)
    {
        int a,b,c;
        cin>>a>>b>>c;
        add(a,b,c),add(b,a,c);
    }
    
    dfs(1,-1);
    
    cout<<f[1][m]<<endl;
    
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章