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

洛谷P2015 二叉蘋果樹

題面:

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

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

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

2   5
 \ / 
  3   4
   \ /
    1

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

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

輸入輸出格式

輸入格式:

第1行2個數, N

N Q ( 1 < = Q < =
N , 1 < N < = 200 ) Q(1<=Q<= N,1<N<=200)

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

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

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

輸出格式:

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

輸入輸出樣例

輸入樣例

5 2
1 3 1
1 4 10
2 3 20
3 5 20

輸出樣例

21

分析:

f [ i ] [ j ] f[i][j] 表⽰以i節點為根,保留j個樹枝最多留下
多少個蘋果。
只給左⼉⼦: f [ i ] [ j ] = f [ l s o n [ i ] ] [ j 1 ] + v l s o n [ i ] f[i][j]=f[lson[i]][j-1]+vlson[i]
只給右⼉⼦: f [ i ] [ j ] = f [ r s o n [ i ] ] [ j 1 ] + v r s o n [ i ] f[i][j]=f[rson[i]][j-1]+vrson[i]
都分⼀點: f [ i ] [ j ] = m a x ( f [ l s o n [ i ] ] [ k ] + f [ r s o n [ i ] ] [ j k 2 ] + v l s o n [ i ] , v r s o n [ i ] ) . 0 < = k < = j 2 f[i][j]=max(f[lson[i]][k]+f[rson[i]][j-k-2]+vlson[i],vrson[i]). 0<=k<=j-2 .

另種思路在程式碼中說明

code:(搜尋+dp)

#include<cstdio>
#include<cmath>
using namespace std;
int n,q;
struct ben
{
	int to,last,val,dq;//to記錄這條邊的終點,last記錄上一條邊,dq記錄邊的起點,val記錄邊的權值
}a[205];
int f[205][205];
int cnt=1;
int head[205];//head[i]表示以i為起點的邊的編號
void add(int x,int y,int num)//連邊函式
{
	a[cnt].to=y;
    a[cnt].last=head[x];
    a[cnt].dq=x;
    a[cnt].val=num;
    head[x]=cnt;
    cnt++;
}
void search(int dq,int father)
{
	for(int i=head[dq];i!=-1;i=a[i].last)
	{
		int b=a[i].to;//找到這條邊的終點
		if(b==father)continue;//不能為起點
		f[b][1]=a[i].val;//初始化
		search(b,dq);//搜尋
		for(int j=q;j>=1;j--)
		{
			for(int k=0;k<=j;k++)
			{
				if((j!=1&&j!=k)||dq==1)
				{
					f[dq][j]=fmax(f[dq][j],f[b][k]+f[dq][j-k]);//列舉中間點k,得出方程
				}
			}
		}
	}
}
int main()
{
	
	scanf("%d%d",&n,&q);
	for(int i=1;i<=n;i++)
	{
		head[i]=-1;
	}//初始化
	for(int i=1;i<n;i++)
	{
		int x,y,num;
		scanf("%d%d%d",&x,&y,&num);
		add(x,y,num);
		add(y,x,num);//注意要連雙向邊
	}
	search(1,0);
	printf("%d",f[1][q]);
	return 0;
}