P2015 二叉苹果树(树上背包)

题目描述

有一棵苹果树,如果树枝有分叉,一定是分2叉(就是说没有只有1个儿子的结点)

这棵树共有N个结点(叶子点或者树枝分叉点),编号为1-N,树根编号一定是1。

我们用一根树枝两端连接的结点的编号来描述一根树枝的位置。下面是一颗有4个树枝的树

2   5
  / 
  3   4
    /
    1

现在这颗树枝条太多了,需要剪枝。但是一些树枝上长有苹果。

给定需要保留的树枝数量,求出最多能留住多少苹果。

输入格式

第1行2个数,N和Q(1<=Q<= N,1<N<=100)。

N表示树的结点数,Q表示要保留的树枝数量。接下来N-1行描述树枝的信息。

每行3个整数,前两个是它连接的结点的编号。第3个数是这根树枝上苹果的数量。

每根树枝上的苹果不超过30000个。

输出格式

一个数,最多能留住的苹果的数量。

题解:

非常入门的树上背包问题,注意跑01背包的时候一定一定要从大到小遍历容量!!!

#include<bits/stdc++.h>
using namespace std;
const int maxn=1010;
typedef long long ll;
int dp[maxn][maxn];//dp(i,j)表示在以i为根的子树里选择j个枝条的最优解 
int n,q;
struct node {
    int u,v,w,nxt;
}edge[maxn];
int head[maxn];
int tot;
int size[maxn];//以i为根的子树的节点数量,枝条为子树节点数-1 
void addedge (int u,int v,int w) {
    edge[tot].u=u;
    edge[tot].v=v;
    edge[tot].w=w;
    edge[tot].nxt=head[u];
    head[u]=tot++;
    
    edge[tot].u=v;
    edge[tot].v=u;
    edge[tot].w=w;
    edge[tot].nxt=head[v];
    head[v]=tot++;
}
void dfs1 (int u,int pre) {
    size[u]=1;
    for (int i=head[u];i!=-1;i=edge[i].nxt) {
        int v=edge[i].v;
        if (v==pre) continue;
        dfs1(v,u);
        size[u]+=size[v];
    }
}
void dfs2 (int u,int pre) {
    for (int i=head[u];i!=-1;i=edge[i].nxt) {
        int v=edge[i].v;
        if (v==pre) continue;
        dfs2(v,u);
        for (int j=size[u];j>=0;j--) {
            for (int k=size[v]-1;k>=0;k--) {
                if (j>=k+1) 
                    dp[u][j]=max(dp[u][j],dp[u][j-k-1]+dp[v][k]+edge[i].w);
            }
        }
    }
}
int main () {
    scanf("%d%d",&n,&q);
    for (int i=1;i<=n;i++) head[i]=-1;
    tot=0;
    for (int i=1;i<n;i++) {
        int x,y,w;
        scanf("%d%d%d",&x,&y,&w);
        addedge(x,y,w);
    }
    dfs1(1,0);
    dfs2(1,0);
    int ans=0;
    for (int i=0;i<=min(q,size[1]);i++) ans=max(ans,dp[1][i]);
    printf("%d
",ans);
}
原文地址:https://www.cnblogs.com/zhanglichen/p/13503370.html