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

Luogu2015 二叉蘋果樹

題目描述

有一棵蘋果樹,如果樹枝有分叉,一定是分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個。

輸出格式:

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

保留Q條邊,即保留Q+1個結點。

我們記f[i][j]為以i為根的子樹中,保留j個結點最多留住蘋果的數量。

這樣這個點的每個子樹都可以不選,或選1~j-1個結點,另一個子樹放剩下的點。

記a[i]為i到它父親的邊上蘋果的數量。

則狀態轉移顯然:f[i][j]=max(f[i][j],f[lc[i]][k]+f[rc[i]][j-k-1]+a[i]) k從0到j。lc和rc分別表示該點的左兒子和右兒子

程式碼:

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#define maxn 200
using namespace std;
int lc[maxn],rc[maxn],f[maxn][maxn],map[maxn][maxn],a[maxn],n,q;
void build(int now)
{
	for(int i=1;i<=n;i++){
		if(map[now][i]>=0){
			lc[now]=i;
			a[i]=map[now][i];
			map[now][i]=-1;	
			map[i][now]=-1;
			build(i);
			break;
		}
	}
	for(int i=1;i<=n;i++){
		if(map[now][i]>=0){
			rc[now]=i;
			a[i]=map[now][i];
			map[now][i]=map[i][now]=-1;
			build(i);
			break;
		}
	}
}
int dfs(int i,int j)
{
	if(j==0) return 0;
	if(!lc[i]&&!rc[i]) return a[i];
	if(f[i][j]) return f[i][j];
	for(int k=0;k<=j-1;k++) f[i][j]=max(f[i][j],dfs(lc[i],k)+dfs(rc[i],j-k-1)+a[i]);
	return f[i][j];
}
int main()
{
	cin>>n>>q;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			map[i][j]=-1;
		}
	}
	for(int i=1;i<n;i++){
		int x,y,z;
		scanf("%d%d%d",&x,&y,&z);
		map[x][y]=z,map[y][x]=z;
	}
	build(1);
	cout<<dfs(1,q+1);
	
	return 0;
}