[Vijos 1676] 陶陶吃蘋果
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]\)
最後按照上面的公式更新即可。
#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] 陶陶吃蘋果