题意:
一棵结点带权树,大小(结点数)为k的子树的权值和最大为多少。
初步分析
这道题其实就是一道01背包问题只是是在树上做而已。背包的总容量就是k个结点(一定得刚好装满),每个物品的价值就是结点的权值w[i].注意,并不是随便选取结点就行了。而是一定得是子树。那么这一点我们要怎么实现呢。首先我们用dp[i][j]来表示以结点i为首的结点数为j的权值最大的一棵子树。那么dp[i][j]的状态方程怎么写呢?dp[i][j]=max(dp[i][j],dp[i][j-w] dp[v][w])(v是结点i的子节点,1=<j<=cnt[i],0<w<j)注意我们是怎么保证dp[i][j]表示的是以结点i为首的子树呢?新节点所能取得的最大结点数只能是j-1,因为dp[i][1]始终代表的是结点i,所以才能保证都是以结点i为首的子树。而且由于是01背包所以我们的背包容量j从大到小更新,这样保证所有的小值都是上一次子节点的更新值而不是重复使用当前结点的值(不然的话就变成完全背包了)。
直接上代码
#include <iostream>
#include <string.h>
using namespace std;
const int MAX_N=110;
const int MAX_M=220;
int w[MAX_N];
int dp[MAX_N][MAX_N];
int cnt[MAX_N];
int n,k,answer=0;
//我们这里用图来储存树,在遍历的时候通过一个father参数就可以转化为树了。没有必要用一个father数组来储存
int ans=0;
int head[MAX_N];
struct edge{
int v;
int next;
}eid[MAX_M];
void insert(int u,int v){
eid[ans].v=v;
eid[ans].next=head[u];
head[u]=ans ;
}
int read(){
int w=1,s=0;
char ch=getchar();
while(!isdigit(ch)){
if(ch=='-'){
w*=-1;
}
ch=getchar();
}
while(isdigit(ch)){
s=(s<<1) (s<<3) ch-48;
ch=getchar();
}
return s*w;
}
int dfs(int node,int father){
cnt[node]=1;
for(int i=head[node];i!=-1;i=eid[i].next){
int v=eid[i].v;
if(v==father) continue;
cnt[node] =dfs(v,node);
}
dp[node][1]=w[node];
for(int i=head[node];i!=-1;i=eid[i].next){
int v=eid[i].v;
if(v==father) continue;
for(int j=cnt[node];j>=1;--j) {
for (int m = 0; m <=cnt[v] && m<j ; m) {
dp[node][j] = max(dp[node][j], dp[node][j - m] dp[v][m]);
}
}
}
answer=max(answer,dp[node][k]);
return cnt[node];
}
int main() {
while(scanf("%d %d",&n,&k)!=EOF) {
for (int i = 1; i <= n; i) {
w[i] = read();
}
memset(head, -1, sizeof(head));
ans=0;
for (int i = 0; i <MAX_N; i) {
for (int j = 0; j <MAX_N; j) {
dp[i][j] = 0;
}
}
for (int i = 1; i <= n - 1; i) {
int x, y;
x = read() 1;
y = read() 1;
insert(x, y);
insert(y, x);
}
dfs(1, -1);
cout <<answer<<endl;
answer=0;
}
return 0;
}
来源:https://www./content-4-386351.html
|