树形背包 学习笔记

时间:2021-10-03
本文章向大家介绍树形背包 学习笔记,主要包括树形背包 学习笔记使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

发现树形背包又忘掉了。。。所以来复习一下顺便写博客。

朴素的树形背包是定义\(f(i,j)\)表示以i为子树用j重量时的最大价值。

那么我们再枚举给每一颗子树分配多少重量,这就是和背包类似的方法了。

#include <cstdio>
#include <algorithm>
using std::max;

const int N = 3e2 + 10, M = 3e2 + 10;
int n, m, f[N][N], s[N], son[N][N];

void dfs (int u) {
    for (int i = 1; i <= son[u][0]; ++i) {
        int v = son[u][i]; dfs(v);
        for (int j = m + 1; j >= 1; --j)
            for (int k = 0; k < j; ++k)
                f[u][j] = max(f[u][j], f[u][j - k] + f[v][k]); 
    }
}

int main () {
    scanf ("%d%d", &n, &m);
    for (int i = 1, fa; i <= n; ++i) {
        scanf ("%d%d", &fa, s + i);
        f[i][1] = s[i];
        son[fa][++son[fa][0]] = i;
    }
    dfs(0);
    printf ("%d\n", f[0][m + 1]);
    return 0;
}

上述代码来自https://www.cnblogs.com/water-mi/p/9818622.html

这是一种比较好的写法,我们先把\(f(i,weight[i])\)预处理出来,以后直接使用,避免麻烦的处理过程!!!!!!!

不过注意当前点u至少要分配weight[u]的重量,转移的时候需要注意,否则会出现不选当前点就选子树的错误。


但是还可以优化,利用dfs序。

可以考虑如果选当前节点,那么它可以更新f(i+1),否则它直接更新f(nex[i]),其中nex[i]为i的下一个兄弟的dfs序。

这里如果考虑f(i,0/1)为当前选和不选转移稍微有点麻烦。我们可以用一个比较好的优化方法,即f(i)表示前i-1个点做完决策时的最大收益,转移的时候更新到f(i+1)的时候再加上第i个点的收益。可以令nex[n]=n+1,这样最后的答案即为f(n+1)

代码如下:

#include<bits/stdc++.h>

using namespace std;

const int N = 1e3 + 9, M = 1e4 + 9;
int n, m, w[N], v[N];

struct Edge{
	int v;
	Edge *nxt;
} e[N];
Edge *head[N];
int e_cnt;
void addEdge(int u, int v){
	e[++e_cnt] = Edge {v, head[u]};
	head[u] = &e[e_cnt];
}

int dfn[N], nx[N], ttime, id[N], f[N][M];
void dfs(int u){
	dfn[u] = ttime;
	id[ttime] = u;
	ttime++;
	for(Edge *i = head[u]; i != NULL; i = i -> nxt)
		dfs(i -> v);
	nx[dfn[u]] = ttime;
}

int main(){
	scanf("%d%d", &n, &m);
	
	for(int i = 1, fa; i <= n; ++i){
		scanf("%d%d%d", w + i, v + i, &fa);
		if(fa == i) fa = 0;
		addEdge(fa, i);
	}
	dfs(0);
	
	for(int i = 2; i <= n + 1; ++i)
		for(int j = 0; j <= m; ++j)
			f[i][j] = -1e9;
	
	for(int u = 1; u <= n; ++u){
		int wt = w[id[u]], vl = v[id[u]];
		for(int j = 0; j <= m; ++j)
			f[nx[u]][j] = std::max(f[nx[u]][j], f[u][j]);
		for(int j = 0; j <= m - wt; ++j)
			f[u + 1][j + wt] = std::max(f[u + 1][j + wt], f[u][j] + vl);
	}
	
	printf("%d\n", f[n + 1][m]);
}

上述代码利用ttime的操作也令了nex[n]=n+1

原文地址:https://www.cnblogs.com/lytql/p/15365204.html