1. 程式人生 > >#123-[樹形動態規劃]二叉蘋果樹

#123-[樹形動態規劃]二叉蘋果樹

Description

有一棵蘋果樹,如果樹枝有分叉,一定是分2叉(就是說沒有隻有1個兒子的結點)。這棵樹共有N個結點(葉子點或者樹枝分叉點),編號為1-N,樹根編號一定是1。

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

現在這顆樹枝條太多了,需要剪枝。但是一些樹枝上長有蘋果。
給定需要保留的樹枝數量,求出最多能留住多少蘋果。

Input

第1行2個數,N和Q(1<=Q<= N,1<N<=100)N表示樹的結點數,Q表示要保留的樹枝數量。接下來N-1行描述樹枝的資訊。
每行3個整數,前兩個是它連線的結點的編號。第3個數是這根樹枝上蘋果的數量。
每根樹枝上的蘋果不超過30000個。

Output

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

Sample Input

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

Sample Output

21

樹形DP的一道很經典的題.

#include <iostream>
#include <vector>

#define SIZE 110

using namespace std;

struct edge
{
	int to, cap;
};

vector<edge> graph[SIZE];
int dp[SIZE][SIZE], m; // DP[I][J]表示第I個節點和它的子樹保留J個樹枝剩餘蘋果的最大值.

int dfs(int u, int pre) // DP過程
{
	int i, j, k, v, w, childcount = 0;
	
	for (i = 0; i < graph[u].size(); ++i)
	{
		v = graph[u][i].to;
		if (v == pre) // 確保不是回到了前一個點
		{
			continue;
		}
		w = graph[u][i].cap;
		childcount += dfs(v, u) + 1;
		for (j = min(childcount, m); j >= 1; --j)
		{
			for (k = min(j, childcount); k >= 1; --k)
			{
				dp[u][j] = max(dp[u][j], dp[u][j-k] + dp[v][k-1] + w); // 條件轉移方程
			}
		}
	}
	
	return childcount; // 順便返回二子數
}

int main(int argc, char** argv)
{
	int n, u, v, w, i;
	
	scanf("%d%d", &n, &m);
	for (i = 1; i < n; ++i)
	{
		scanf("%d%d%d", &u, &v, &w);
		graph[u].push_back({v, w}); // 建圖
		graph[v].push_back({u, w});
	}
	
	dfs(1, -1);
	
	printf("%d", dp[1][m]);
		
	return 0;
}