搜集钻石(树形DP经典例题)

搜集钻石

Description

 

蒜国有 n 座城市,编号从 1 到 n,城市间有 n−1 条道路,且保证任意两座城市之间是连通的。每一座城市有一定数量的钻石。

蒜头君想在蒜国搜集钻石。他从城市 1 出发,每天他可以通过城市之间道路开车到另外的城市。当蒜头第一次到一个城市的时候,他可以搜集完这个城市的所有钻石,如果他后面再来到这个城市,就没有砖石可以收集了。

蒜头君只有 K 天时间,请你帮算蒜头君计算他最多可以搜集多少钻石。

Input

 

第一行输入三个整数 n(1≤n≤100),K(0≤k≤200),表示一共有 n 座城市,蒜头君有 K 天时间。

接下里一行输入 n 个用空格隔开的整数,表示每个城市的钻石数量。每个城市的钻石数量不大于 1000。

接下来输入 n−1 行,每行输入两个整数 a(1≤a≤n),b(1≤b≤n),表示城市 a和城市 b 之间存在一条双向道路。

Output

 

输出一行一个整数表示蒜头君最大能获取的钻石数量。

Sample Input 1

3 2
3 8 3
1 3
3 2

Sample Output 1

14

Sample Input 2

6 2
5 9 8 4 9 2
1 6
6 2
2 5
5 3
5 4

Sample Output 2

16

Source

计蒜客

题意分析:有n个节点,n-1条边,联通,很容易就可以想到是一棵树,又让求最大值,自然而然的就联想到了树形DP;则原题就变成了——给你一棵树,每条边(双向)的权值为一,每个节点有一定价值且只能取一次(注意去了一定还要回来才可以到同层的另一子树),有看k天,问能取到的最大价值;

题目分析:对于每个子树,它的子树可以去到不止一个(在时间够的情况下),且对于每棵子树有两种操作——去了回来和去了不会;去了回来的情况比较好考虑——如果根节点去了要回,则它的每个子树去了都要回,于是很容易就可以敲出一个树上的分组背包(具体操作不赘述);但麻烦的在不回来的情况——可以有很多个回来的字树,但只能有一个不回来的子树,根据惯性思维,在想出了多重背包解回来情况之后,便会在想到确定一个不回来的子树,再在剩下的子树中做一次分组背包,取最大值,我就收这种垃圾思维的影响苦苦肝了很久(欲哭无泪),接下来先送上我的70分代码(其实细节处理好了应该可以满分):

#include
using namespace std;
int n,m,ma[1100],dp[1100][2100][2];
vector vec[1100];
void dfs(int a,int b,int c,int fa)
{//cout<=2;j-=2)
			{
				for(int e=0;e<=j;e+=2)
				{
					dp2[j]=max(dp2[j],dp2[j-e]+dp[vec[a][i]][e][1]);
					dp2[j+1]=dp2[j];
					//cout<=2;j-=2)
				{
					for(int e=0;e<=j;e+=2)
					{
						dp2[j]=max(dp2[j],dp2[j-e]+dp[vec[a][i]][e][1]);
						dp2[j+1]=dp2[j];
					}
				}
			}
			for(int i=1;i<=b;i++)
			{
				for(int j=1;j<=i-1;j++)	
				{//cout<>n>>m;
	for(int i=1;i<=n;i++)
	{
		cin>>ma[i];
	}
	for(int i=1;i>a>>b;
		vec[a].push_back(b);
		vec[b].push_back(a);
	}
	dfs(1,m,1,-2);
	dfs(1,m,0,-2);
	cout<

有点长(还没过......);

接下来说一说正解思路(很简洁):首先当然要赋初值dp[u][a][b]统统赋成u节点的值,然后开始枚举子节点(注意不要枚举到父节点了),再递归到下一层的dp[v][a][b](没有它解决不了大问题,具体过程不要深想),再枚举0->m(m为最大天数),再枚举给前面已打出的量的天数e(0->j-1),前面已打出的和当前的又只能有一个去了不回来,于是不难列出方程:要回来的情况:dp[u][j][1]=max(dp[u][j][1],dp[u][e][1]+dp[v][j-e-2][1]); 不回来的情况:dp[u][j][0]=max(dp[u][j][0],dp[u][e][0]+dp[v][j-e-2][1]);dp[u][j][0]=max(dp[u][j][0],dp[u][e][1]+dp[v][j-e-1][0]);

AC代码:

#include
using namespace std;
int n,m,dp[210][210][2],ma[210];
vector vec[210];
void dfs(int fa,int u)
{
	int l=vec[u].size();
	for(int i=0;i<=m;i++)
	{
		dp[u][i][1]=dp[u][i][0]=ma[u];
	}
	for(int i=0;i=1;j--)
		{
			for(int e=0;e<=j-1;e++)
			{
				if(j-e>=2)
				{
					dp[u][j][1]=max(dp[u][j][1],dp[u][e][1]+dp[v][j-e-2][1]);
					dp[u][j][0]=max(dp[u][j][0],dp[u][e][0]+dp[v][j-e-2][1]);//前面的不回来
				}
				dp[u][j][0]=max(dp[u][j][0],dp[u][e][1]+dp[v][j-e-1][0]);//后面的不回来
			}
		}
	}
}
int main()
{
	cin>>n>>m;
	for(int i=1;i<=n;i++)
	{
		cin>>ma[i];
	}
	for(int i=1;i>a>>b;
		vec[a].push_back(b);
		vec[b].push_back(a);
	}
	dfs(-1,1);
	cout<

 

你可能感兴趣的:(C,树形DP,背包)