题目1 : 刷油漆
时间限制:10000ms
单点时限:1000ms
内存限制:256MB
描述
上回说到,小Ho有着一棵灰常好玩的树玩具!这棵树玩具是由N个小球和N-1根木棍拼凑而成,这N个小球都被小Ho标上了不同的数字,并且这些数字都是处于1..N的范围之内,每根木棍都连接着两个不同的小球,并且保证任意两个小球间都不存在两条不同的路径可以互相到达。没错,这次说的还是这棵树玩具的故事!
小Ho的树玩具的质量似乎不是很好,短短玩了几个星期,便掉漆了!
“简直是一场噩梦!”小Ho拿着树玩具眼含热泪道。
“这有什么好忧伤的,自己买点油漆刷一刷不就行了?”小Hi表示不能理解。
“还可以这样?”小Ho顿时兴高采烈了起来,立马跑出去买回来了油漆,但是小Ho身上的钱却不够——于是他只买回了有限的油漆,这些油漆最多能给M个结点涂上颜色,这就意味着小Ho不能够将他心爱的树玩具中的每一个结点都涂上油漆!
小Ho低头思索了半天——他既不想只选一部分结点补漆,也不想找小Hi借钱,但是很快,他想出了一个非常棒的主意:将包含1号结点的一部分连通的结点进行涂漆(这里的连通指的是这一些涂漆的结点可以互相到达并且不会经过没有涂漆的结点),然后将剩下的结点拆掉!
那么究竟选择哪些结点进行涂漆呢?小Ho想了想给每个结点都评上了分——他希望最后留下来,也就是涂漆了的那些结点的评分之和可以尽可能的高!
那么,小Ho该如何做呢?
输入
每个测试点(输入文件)有且仅有一组测试数据。
每组测试数据的第一行为两个整数N、M,意义如前文所述。
每组测试数据的第二行为N个整数,其中第i个整数Vi表示标号为i的结点的评分
每组测试数据的第3~N+1行,每行分别描述一根木棍,其中第i+1行为两个整数Ai,Bi,表示第i根木棍连接的两个小球的编号。
对于100%的数据,满足N<=10^2,1<=Ai<=N, 1<=Bi<=N, 1<=Vi<=10^3, 1<=M<=N
小Hi的Tip:那些用数组存储树边的记得要开两倍大小哦!
输出
对于每组测试数据,输出一个整数Ans,表示使得涂漆结点的评分之和最高可能是多少。
样例输入
10 4 370 328 750 930 604 732 159 167 945 210 1 2 2 3 1 4 1 5 4 6 4 7 4 8 6 9 5 10
样例输出
2977
比赛已经结束,去题库提交。
解题思路:定义dp[i][m]为必选根节点i容量为m的时候能得到的最大价值。
那么我们可以由i的个个子节点的dp值转移过来。
引用背包九讲中泛化物品的概念,这个时候i的每个子节点相当于一个泛化物品(每一个容量对应一个价值),而我们的
目的是合并两个泛化物品。
合并两个泛化物品的方法就是枚举当前的容量再枚举分配给两个物品的容量即可。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
using namespace std;
#define LL long long
#define sca(x) scanf("%d",&x)
#define pb(x) push_back(x)
#define N 205
int w[N];
vector<int>V[N];
int dp[N][N];
int m;
void dfs(int u,int fa)
{
dp[u][1]=w[u];
for(int i=0;i<V[u].size();i++)
{
int v=V[u][i];
if(v==fa)continue;
dfs(v,u);
for(int i=m;i>=1;i--)//注意这里要倒序枚举(因为我们不能用本次更新过的更新本次)。
{
for(int j=1;j<=i;j++)
{
dp[u][i]=max(dp[u][i],dp[u][j]+dp[v][i-j]);
}
}
}
}
int main()
{
int n;
sca(n),sca(m);
for(int i=1;i<=n;i++)sca(w[i]);
for(int i=1;i<n;i++)
{
int x,y;
sca(x),sca(y);
V[x].pb(y);
V[y].pb(x);
}
dfs(1,0);
printf("%d\n",dp[1][m]);
}