树上背包:
树形背包就是原始的树上动归+背包,一般用来处理以一棵树中选多少点为扩展的一类题,基本做法与树上dp无异,不过在状态转移方程中会用到背包的思想。
它基本上是这个样子的:
存图),然后dfs进去补全子节点的信息,f数组的意思是以fa为中转点,找出fa往下的可取1~j个点时各自的最大收益。
for(int i=1;i<=son;i++){//枚举所有儿子 dfs(son[i]);//先处理儿子 for(int j1=m;j1>=0;j1--){//枚举当前点用掉了多少容量(正着枚举会变成完全背包) for(int j2=0;j2<=j1;j2++){//枚举这个儿子分配多少 dp[i][j1]=max(dp[i][j1],dp[i][j1-j2]+dp[son[i]][j2]);//更新状态 } } }
选课这题基本上就是树上背包的板子题了:
#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; }
还有一种多叉树转二叉树的方法:
#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; int f[maxn][maxn],mark[maxn]; int l[maxn],r[maxn]; bool s[maxn][maxn]; int w[maxn]; int n,m; inline void pre(int x) { // 多叉树转二叉树 mark[x] = 1; for (int i = 1;i <= n+1;i++) { if (!mark[i] && s[x][i]) { r[i] = l[x]; l[x] = i; pre(i); } } } inline void dfs(int x) { if (l[x]) dfs(l[x]); if (r[x]) dfs(r[x]); for (int y = 0;y <= m;y++) { f[x][y] = f[r[x]][y]; for (int k = 0;k <= y-1;k++) f[x][y] = max(f[x][y],f[l[x]][k]+f[r[x]][y-k-1]+w[x]); } } int main() { ios::sync_with_stdio(false); cin >> n >> m; for (int i = 1;i <= n;i++) { int a,b; cin >> a >> b; w[i] = b; if (a == 0) a = n+1; s[a][i] = true; } pre(n+1); dfs(n+1); cout << max(f[l[n+1]][m],f[r[n+1]][m]) << endl; return 0; }