题目
一共N个结点的树(1至N编号),油漆能够涂M个结点。
将包含1号结点的一部分连通的结点进行涂漆(这里的连通指的是这一些涂漆的结点可以互相到达并且不会经过没有涂漆的结点),然后将剩下的结点拆掉!
那么究竟选择哪些结点进行涂漆呢?小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,表示使得涂漆结点的评分之和最高可能是多少。
思路
从结点1思考。它有 children(1) 个子结点。 那么对于每个子结点能分配到的油漆的总和就是M。
可以把本题看做一个完全背包的变体,背包容量是M,每个子结点分配的1个油漆数量占的体积是1,只是价值会根据分配的油漆数量而变化。
代码
最近试着模仿一些大神的代码风格。
#include<cstdio>
#include<cstring>
#define ree(i,cur) for(int i=head[cur];i!=-1;i=edge[i].next)
#define red(i,s,t) for(int i=s;i>t;i--)
#define rep(i,s,t) for(int i=s;i<t;i++)
#define clr(a,v) memset(a,v,sizeof a)
using namespace std;
const int MAX_N=100+5;
struct EDGE{
int v,next;
}edge[MAX_N];
int e,head[MAX_N];
int N,M;
int ans[MAX_N][MAX_N];
inline void addEdge(int u,int v){
edge[e].v=v;
edge[e].next=head[u];
head[u]=e++;
}
void f(int t)
{
ree(n,t){
f(edge[n].v);
}
ree(n,t)
{
red(m,M,1)
rep(i,1,m)
{
int t1=ans[t][m-i]+ans[edge[n].v][i];
if(t1>ans[t][m]) ans[t][m]=t1;
}
}
}
int main()
{
int a,b;
clr(ans,0);
clr(edge,0);
clr(head,-1);
scanf("%d%d",&N,&M);
rep(i,1,N+1){scanf("%d",&ans[i][1]);}
rep(i,1,N)
{
scanf("%d%d",&a,&b);
addEdge(a,b);
}
f(1);
printf("%d",ans[1][M]);
return 0;
}