天天看點

CJOJ 1976 二叉蘋果樹 / URAL 1018 Binary Apple TreeCJOJ 1976 二叉蘋果樹 / URAL 1018 Binary Apple Tree(樹型動态規劃)

CJOJ 1976 二叉蘋果樹 / URAL 1018 Binary Apple Tree(樹型動态規劃)

Description

有一棵蘋果樹,如果樹枝有分叉,一定是分2叉(就是說沒有隻有1個兒子的結點)這棵樹共有N個結點(葉子點或者樹枝分叉點),編号為1-N,樹根編号一定是1。我們用一根樹枝兩端連接配接的結點的編号來描述一根樹枝的位置。現在這顆樹枝條太多了,需要剪枝。但是一些樹枝上長有蘋果。

給定需要保留的樹枝數量,求出最多能留住多少蘋果。下面是一顆有 4 個樹枝的樹。

2 5

\ /

3 4

\ /

1

Input

第1行2個數,N和Q(1<=Q<= N,1<N<=100)。N表示樹的結點數,Q表示要保留的樹枝數量。

接下來N-1行描述樹枝的資訊,每行3個整數,前兩個是它連接配接的結點的編号。第3個數是這根樹枝上蘋果的數量。

每根樹枝上的蘋果不超過30000個。

Output

剩餘蘋果的最大數量。

Sample Input

5 2

1 3 1

1 4 10

2 3 20

3 5 20

Sample Output

21

Http

CJOJ:http://oj.changjun.com.cn/problem/detail/pid/1976

URAL:https://vjudge.net/problem/URAL-1018

Source

樹型動态規劃

解決思路

我們定義F[u][j]表示以u為根節點的子樹保留j條邊所能保留的最大蘋果數,設v為其子節點。

首先我們想到的狀态轉移方程是F[u][j]=max(F[u][j],F[v][j-1]+W[u][v]),但是這樣方程的意思是這個狀态隻從子樹j轉移過來,但這樣是不行的。假設u有3個子節點v1,v2,v3,j為5的時候,如果按照上面的方法,就是取F[v1][4],F[v2][4],F[v3][4]中的最大值+這條邊上的權值,但F[u][5]還可以從F[v1][1]+F[v2][1]+F[v3][2]再加上這條邊上的權值這類轉移過來,是以這個轉移方程是不對的。

那麼我們仍定義F[u][j]表示以u為根節點的子樹保留j條邊所能保留的最大蘋果樹,定義v為目前要計算的u的一個子節點,另外定義k-1為從以v為根節點的子樹中取k-1條邊,那麼就從i之前的子節點的子樹集合中取j-k條邊(因為要滿足v中選的邊+原來那些點中選的邊+1==j,要+1的原因是因為u->j這條邊是必選的),再取max即可。

s是以動态轉移方程就是F[u][j]=max(F[u][j],F[u][j-k]+F[v][k-1]+W[u][v]);

;最後要注意的就是求上述F[u][j]時循環k的順序,一定要從大到小,否則會出現重複計算。

代碼

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;

class Edge
{
public:
    int v,w;
};

const int maxN=;
const int inf=;

int n,Q;
vector<Edge> E[maxN];
bool vis[maxN];
int F[maxN][maxN]={};

int dfs(int u);

int main()
{
    int u,v,w;
    cin>>n>>Q;
    for (int i=;i<n;i++)
    {
        cin>>u>>v>>w;
        E[u].push_back((Edge){v,w});
        E[v].push_back((Edge){u,w});
    }
    memset(vis,,sizeof(vis));
    dfs();
    cout<<F[][Q]<<endl;
    return ;
}

int dfs(int u)
{
    int cnt=;//統計子樹枝個數
    vis[u]=;
    for (int i=;i<E[u].size();i++)
    {
        int v=E[u][i].v;
        if (vis[v]==)
        {
            cnt+=dfs(v)+;//每次要+1,即加上u->v這條邊
            for (int j=min(Q,cnt);j>=;j--)//要取min的原因是要簡化循環
                for (int k=j;k>=;k--)
                {
                    F[u][j]=max(F[u][j],F[u][j-k]+F[v][k-]+E[u][i].w);
                }
        }
    }
    return cnt;
}