1. 程式人生 > >[Vijos 1676] 陶陶吃蘋果

[Vijos 1676] 陶陶吃蘋果

價值 max 這樣的 ont dig sdi 超過 signed 觀察

Description

curimit知道陶陶很喜歡吃蘋果。於是curimit準備在陶陶生日的時候送給他一棵蘋果樹。

curimit準備了一棵這樣的蘋果樹作為生日禮物:這棵蘋果樹有n個節點,每個節點上有c[i]個蘋果,這棵樹高度為h。

可是,當curimit把這棵樹給陶陶看的時候,陶陶卻說:“今年生日不收禮,收禮只收節點數減高度不超過k的蘋果樹。”這下curimit犯難了,curimit送來的樹枝繁葉茂,不滿足節點數-高度≤k。於是curimit決定剪掉一些枝條,使得修剪過後的樹滿足節點數-高度≤k,但是curimit又想保留盡量多的蘋果數目。curimit想請你幫他算算經過修剪後的樹最多能保留多少個蘋果。

註:
一, 節點1為樹根,不能把它剪掉。

二, 1個節點的樹高度為1。

Input

輸入文件的第一行為兩個整數n,k分別表示這棵樹有n個節點,修剪後的樹節點數-高度≤k。

第二行開始到第n+1行,每行有兩個數,第i+1行的兩個數father[i]和c[i]分別表示節點i的父親是father[i]和節點i處有c[i]個蘋果。

規定:節點1的父親為0。

Output

輸出文件僅包含一行,ans,表示在滿足修建後的樹節點數-高度≤k的條件下,最多能保留多少個蘋果。

Hint

對於100%的數據,n≤4000 0≤k≤=500

Solution

翻譯一下題目,就是讓你選一條鏈,然後再選 \(k\) 個點,使得這一條鏈上的點權加上選出來的點權和最大。

顯然樹形 \(dp\)

觀察到以下性質:選出來的鏈,一定是從根節點到葉子節點的某條鏈。如果不這樣選,總能找到更優的選法使總答案變大。

所以對於樹上每條到葉子的鏈,通過樹形 \(dp\) 找出以這條鏈為界限向左和向右選 \(j\) 個點能獲得的最大收益,那麽最後答案就是 \(\max \limits_{sons[i]=0}(sum[i]+\max\limits_{0\leq j\leq k}f[i][j]+g[i][k-j])\)

這個 \(f\) 數組怎麽求呢?

\(dfs\) 的過程中,搜完一條鏈並回溯之後方可繼續搜下一條鏈。那麽在搜當前鏈的過程中,根節點裏存的信息實際上是上一條完整的鏈的信息,即 \(f[1][j]\)

表示當前這條鏈左邊所有點中選 \(j\) 個的最大點權和,於是我們就從一號點一路將這個 \(f\) 數組放進葉子節點,然後再從葉子節點向上更新選當前這條鏈上某個點的最大價值,這就求完了 \(f\) 數組。 \(g\) 數組也是同理。

最後按照上面的公式更新即可。

#include<cstdio>
#include<cctype>
#define N 4005
#define max(A,B) ((A)>(B)?(A):(B))
#define min(A,B) ((A)<(B)?(A):(B))

int val[N];
int deg[N];
int sum[N];
int cnt,n,m;
int f[N][505];
int g[N][505];
int son[N][N];

int getint() {
    int x=0;char ch=getchar();
    while(!isdigit(ch)) ch=getchar();
    while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
    return x;
}

void dfs1(int now){
    sum[now]+=val[now];
    for(int i=1;i<=son[now][0];i++){
        int to=son[now][i];
        for(int j=0;j<=m;j++)
            f[to][j]=f[now][j];
        sum[to]=sum[now];
        dfs1(to);
        for(int j=1;j<=m;j++)
            f[now][j]=max(f[now][j],f[to][j-1]+val[to]);
    }
}

void dfs2(int now){
    for(int i=son[now][0];i;i--){
        int to=son[now][i];
        for(int j=0;j<=m;j++)
            g[to][j]=g[now][j];
        dfs2(to);
        for(int j=1;j<=m;j++)
            g[now][j]=max(g[now][j],g[to][j-1]+val[to]);
    }
}

signed main() {
    n=getint(),m=getint();
    for(int i=1;i<=n;i++) {
        if(i==1){
            getint(),val[1]=getint();
            continue;
        } else {
            int a=getint();
            son[a][++son[a][0]]=i;
            val[i]=getint();
        }
    }
    dfs1(1);
    dfs2(1);
    int ans=0;
    for(int i=1;i<=n;i++){
        if(son[i][0])
            continue;
        for(int j=0;j<=m;j++)
            ans=max(ans,f[i][j]+g[i][m-j]+sum[i]);
    }
    printf("%d\n",ans);
    return 0;
}

[Vijos 1676] 陶陶吃蘋果