題目
一共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;
}