树上背包:
树形背包就是原始的树上动归+背包,一般用来处理以一棵树中选多少点为扩展的一类题,基本做法与树上dp无异,不过在状态转移方程中会用到背包的思想。
它基本上是这个样子的:
存图),然后dfs进去补全子节点的信息,f数组的意思是以fa为中转点,找出fa往下的可取1~j个点时各自的最大收益。
void dfs(int fa){ for(int i=0;i<son[fa].size();i++){ int ny=son[fa][i]; dfs(ny); for(int j=m+1;j>=1;j--){ for(int k=j-1;k>=0;k--){ f[fa][j]=max(f[fa][j],f[fa][j-k]+f[ny][k]); } } } }
选课这题基本上就是树上背包的板子题了:
#include <iostream> #include <algorithm> #include <string> #include <string.h> #include <vector> #include <map> #include <stack> #include <set> #include <queue> #include <math.h> #include <cstdio> #include <iomanip> #include <time.h> #include <bitset> #include <cmath> #define LL long long #define INF 0x3f3f3f3f #define ls nod<<1 #define rs (nod<<1)+1 const double eps = 1e-10; const int maxn = 310; const LL mod = 1e9 + 7; int sgn(double a){return a < -eps ? -1 : a < eps ? 0 : 1;} using namespace std; struct edge { int v,nxt; }e[maxn]; int head[maxn],w[maxn],f[maxn][maxn]; int fa[maxn]; int cnt; int n,m; void add_edge(int u,int v) { e[++cnt].v = v; e[cnt].nxt = head[u]; head[u] = cnt; } void dfs(int x) { for (int i = head[x];~i;i = e[i].nxt) { int v = e[i].v; dfs(v); for (int j = m+1;j >= 1;j--) { for (int k = j-1;k >= 0;k--) f[x][j] = max(f[x][j],f[v][k]+f[x][j-k]); } } } int main() { cnt = 0; memset(head,-1, sizeof(head)); cin >> n >> m; for (int i = 1;i <= n;i++) { int u,v; cin >> u >> v; fa[i] = u; add_edge(u,i); f[i][1] = v; } dfs(0); cout << f[0][m+1] << endl; return 0; }