1. 程式人生 > >P2015 二叉蘋果樹

P2015 二叉蘋果樹

現在 初始化 ora void 之前 ... cout 比較 orange

題目描述

有一棵蘋果樹,如果樹枝有分叉,一定是分2叉(就是說沒有只有1個兒子的結點)

這棵樹共有N個結點(葉子點或者樹枝分叉點),編號為1-N,樹根編號一定是1。

我們用一根樹枝兩端連接的結點的編號來描述一根樹枝的位置。下面是一顆有4個樹枝的樹

2   5
 \ / 
  3   4
   \ /
    1

現在這顆樹枝條太多了,需要剪枝。但是一些樹枝上長有蘋果。

給定需要保留的樹枝數量,求出最多能留住多少蘋果。

輸入輸出格式

輸入格式:

第1行2個數,N和Q(1<=Q<= N,1<N<=100)。

N表示樹的結點數,Q表示要保留的樹枝數量。接下來N-1行描述樹枝的信息。

每行3個整數,前兩個是它連接的結點的編號。第3個數是這根樹枝上蘋果的數量。

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

輸出格式:

一個數,最多能留住的蘋果的數量。

輸入輸出樣例

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

Solution

一道我想了比較久的樹形DP.

本來以為就是常規的和選課差不多的基本樹形DP.

於是想出來一個思路:

f [i][j] 表示當前以當前這個節點為根的子樹,刪掉了 j 條邊的最大值.

然後我想的是先預處理每一個節點的邊之和和果子之和,結果發現妥妥的後效性...

於是,想到常規思路.不過為了取消對於當前這個邊是否為當前這個子樹的根的連邊的考慮.

f [i][j] 表示當前這個節點一定被保存下來.

然後的話狀態轉移也就很簡單了,就是一個類似於背包的轉移方程.

但是,我打的時候,之前一直是 0 .

結果發現,我對已經初始化了的 f[x][1],仍然進行了更新...

代碼

#include<bits/stdc++.h>
using namespace std;
const int maxn=105;
struct sj{
    int to;
    int next;
    int w;
}a[maxn
*2]; int size,head[maxn]; int c[maxn],n,m,num[maxn]; int f[maxn][maxn],v[maxn]; void add(int x,int y,int z) { a[++size].to=y; a[size].next=head[x]; head[x]=size; a[size].w=z; } /*void pre(int x) { for(int i=head[x];i;i=a[i].next) { int tt=a[i].to; if(!c[tt]&&tt!=1) { pre(tt); num[x]+=num[tt]; } } }*/ //沒卵用的初始化. void dp(int x) { v[x]=1; for(int i=head[x];i;i=a[i].next) { int tt=a[i].to; if(!v[tt]) { f[tt][1]=a[i].w; dp(tt); for(int j=m;j>=1;j--) for(int k=0;k<=j;k++) if((k!=j&&j!=1)||x==1) f[x][j]=max(f[x][j],f[tt][k]+f[x][j-k]); } } } int main() { scanf("%d%d",&n,&m); for(int i=1;i<n;i++) { int x,y,z; scanf("%d%d%d",&x,&y,&z); add(x,y,z); add(y,x,z); } add(0,1,0); pre(0); //memset(f,-1,sizeof(f)); dp(0); cout<<f[1][m]<<endl; }

P2015 二叉蘋果樹