树上背包学习笔记

0.前言

有一说一,不如先学习泛化物品背包,再学树上背包不是有手就行......吗?

没错,你被骗了,这其实是一篇泛化物品背包学习笔记。但是学习这个之后再去理解树上背包以及推广会轻而易举。

没错,如你所见,这个人一开始用泛化物品概念理解树上背包,但理解透了之后发现自己是个naiver,于是接受了治疗,现已直接能理解树上背包。

1.正文

对于一般的树上背包

\(f_{i,j}\)表示以\(i\)为根的子树花费\(j\)的代价所能获得的最大价值

\[f_{i,j}=\max_{\sum_{son_i} W_i=j-1}\left \{\sum_{son_i}V_i \right \} \]

理论上这个方程应该长这样,然而这个方程极其恶心,基本上没人想用。

这时我们可以将子树看成一个个物品,那么就转化为了背包问题。

其实这还是到了泛化物品背包的范畴,然而我懒得再改回去了,关于这个我会另写一篇文章的

看一看\(01\)背包的动规方程

\[f_{i,j}=\max\begin{cases} f_{i-1,j}\\ f_{i-1,j-V_i}+W_i&{\mathrm{if}\enspace j\geq V_i} \end{cases} \]

我们发现只需要在原来的基础上再加一维,即当前子树的根节点,就可以类似\(01\)背包来解决问题了。

\(f_{i,j,k}\)表示以\(i\)为根的子树在前\(j\)个子节点花费\(k\)的代价所能获得的最大价值

\[f_{i,j,k}=\max_{0\leq x\leq \min_ \{V_{j},k \} }\{ f_{i,j-1,k-x}+f_{j,size_j,x}\} \]

这样看就干净多了,很明显可以用滚动数组优化。

事实上,树上背包还有另外一种表现形式

\[f_{i,j,x+y}=f_{i,j,x}\cup f_{j,size_j,y} \]

这里的并运算指的是将两部分贡献合并,不是集合的并运算。两种方法类似于填表法与刷表法的区别,根据需要选择。

例题1:P2014 [CTSC1997] 选课

这题是个裸的树上背包,只需要建一个\(0\)虚根就能解决

#include<bits/stdc++.h>
#define ll long long
#define ull unsigned long long 
using namespace std;
const int MAXN=300+3;
int n,m,val[MAXN],f[MAXN][MAXN];
int size[MAXN];
vector<int> tree[MAXN];
void dfs(int num,int fa){
	size[num]=1;
	f[num][1]=val[num];
	for(auto i:tree[num]){
		if(i==fa) continue;
		dfs(i,num);
		size[num]+=size[i];
		for(int j=min(size[num],m+1);j>=1;j--){
			for(int k=0;k<j;k++){
				f[num][j]=max(f[num][j],f[num][j-k]+f[i][k]);
			}
		}
	}
}
int main(){
    ios::sync_with_stdio(false);
	cin>>n>>m;
	for(int i=1;i<=n;i++){
		int k,s;
		cin>>k>>s;
		tree[k].push_back(i);
		val[i]=s;
	}
	dfs(0,-1);
	cout<<f[0][m+1]<<endl;
    return 0;
}

例题2:P2015 二叉苹果树

同样是一道裸题

#include<bits/stdc++.h>
#define ll long long
#define ull unsigned long long 
using namespace std;
const int MAXN=1e2+3;
int n,q,ans;
struct Node{
	int v,val;
	Node(int a,int b){v=a,val=b;}
};
vector<Node> tree[MAXN];
int f[MAXN][MAXN];
int size[MAXN];
void dfs(int num,int fa){
    for(auto i:tree[num]){
	if(i.v==fa) continue;
       	dfs(i.v,num);
        size[num]+=size[i.v]+1;
	for(int j=min(q,size[num]);j>=0;j--){
	    for(int k=min(j-1,size[i.v]);k>=0;k--){
		f[num][j]=max(f[num][j],f[num][j-k-1]+f[i.v][k]+i.val);
            }
	}
    }
}
int main(){
    ios::sync_with_stdio(false);
    cin>>n>>q;
    for(int i=1;i<n;i++){
    	int u,v,w;
    	cin>>u>>v>>w;
    	tree[u].push_back(Node(v,w));
    	tree[v].push_back(Node(u,w));
    }
    dfs(1,0);
    cout<<f[1][q]<<endl;
    return 0;
}

不如做做下面几道
CF161D Distance in Tree(比较水,不过要多维护一个东西)

CF877E Danil and a Part-time Job(不是那么水,但只要发现一个性质就相当简单)

P4516 [JSOI2018] 潜入行动
(配的上评分的题,需要大力分类讨论,还有一些坑)

posted @ 2022-11-08 21:33  cmd_pig  阅读(68)  评论(0)    收藏  举报