题目链接
https://www.luogu.org/problemnew/show/P2014
分析
巧妙的树上背包,$f[now][p]$表示$now$节点选了$p$门课所得最大收益
将$now$节点的每个子节点$v$看成一组物品,一组物品有$size[v]$个物品,每个物品的体积为$1$,选了$j$个物品的价值为$f[v][j]$
于是这样写状态转移
1 2 3 4 5
| for(ri i=m;i>=0;i--){ for(ri j=0;j<=i;j++){ f[now][i]=max(f[now][i],f[now][i-j]+f[v][j]); } }
|
当然不要忘记把取$now$节点的贡献加入$f$中
同时注意到一开始形态可能是个森林,按照套路搞个$n+1$的虚点就好了
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61
| #include <cstdio> #include <cstdlib> #include <algorithm> #include <cctype> #include <iostream> #define ll long long #define ri register int using std::min; using std::max; template <class T>inline void read(T &x){ x=0;int ne=0;char c; while(!isdigit(c=getchar()))ne=c=='-'; x=c-48; while(isdigit(c=getchar()))x=(x<<3)+(x<<1)+c-48; x=ne?-x:x;return; } const int maxn=1305; const int inf=0x7fffffff; struct Edge{ int ne,to; }edge[maxn<<1]; int h[maxn],num_edge=0; inline void add_edge(int f,int to){ edge[++num_edge].ne=h[f]; edge[num_edge].to=to; h[f]=num_edge; } int f[maxn][maxn],n,m; int a[maxn]; void dfs(int now){ int v; f[now][0]=0; for(ri i=h[now];i;i=edge[i].ne){ v=edge[i].to; dfs(v); for(ri i=m;i>=0;i--){ for(ri j=0;j<=i;j++){ f[now][i]=max(f[now][i],f[now][i-j]+f[v][j]); } } } if(now!=n+1){ for(ri i=m;i>=1;i--){ f[now][i]=f[now][i-1]+a[now]; } } return ; } int main(){ int x,k,y; read(n),read(m); for(ri i=1;i<=n;i++){ read(k),read(x); a[i]=x; if(!k) add_edge(n+1,i); else add_edge(k,i); } dfs(n+1); printf("%d\n",f[n+1][m]); return 0; }
|