树形dp就是....我也不知道是什么,反正一个主件下面有不少的附件可选就是树形dp,咕咕咕node
树形dp的主要实现形式是dfs,在dfs中dp,主要的实现形式是dp[i][j](j=0或者是1),i是以i为根的子树,j是表示在以i为根的子树中选择j个子节点,0表示这个节点不选,1表示选择这个节点。有的时候j或0/1这一维能够压掉ios
选择节点类:
\[dp[i][0]=dp[j][1] dp[i][1]=max/min(dp[j][0],dp[j][1])\]数组
树形背包类:
\[dp[v][k]=dp[u][k]+val dp[u][k]=max(dp[u][k],dp[v][k−1])\]app
其实我以为就是这样的:
dp[i][j]并不表示以i为根,选j个物品,而是从根节点到i的路径及其左边全部的节点,以及以i为根的子树的全部节点中,容量为j的最大价值。上述方程应该用到了泛化物品的方法函数
定义
考虑这样一种物品,它并无固定的费用和价值,而是它的价值随着你分配给它的费用而变化。这就是泛化物品的概念。学习
更严格的定义之。在背包容量为V的背包问题中,泛化物品是一个定义域为0..V中的整数的函数h,当分配给它的费用为v时,能获得的价值就是h(v)。ui
这个定义有一点点抽象,另外一种理解是一个泛化物品就是一个数组h[0..V],给它费用v,可获得价值h[V]。spa
一个费用为c价值为w的物品,若是它是01背包中的物品,那么把它当作泛化物品,它就是除了h(c)=w其它函数值都为0的一个函数。若是它是彻底背包中的物品,那么它能够当作这样一个函数,仅当v被c整除时有h(v)=v/cw,其它函数值均为0。若是它是多重背包中重复次数最多为n的物品,那么它对应的泛化物品的函数有h(v)=v/cw仅当v被c整除且v/c<=n,其它状况函数值均为0。code
一个物品组能够看做一个泛化物品h。对于一个0..V中的v,若物品组中不存在费用为v的的物品,则h(v)=0,不然h(v)为全部费用为v的物品的最大价值。P07中每一个主件及其附件集合等价于一个物品组,天然也可看做一个泛化物品。blog
泛化物品的和
若是面对两个泛化物品h和l,要用给定的费用从这两个泛化物品中获得最大的价值,怎么求呢?事实上,对于一个给定的费用v,只需枚举将这个费用如何分配给两个泛化物品就能够了。一样的,对于0..V的每个整数v,能够求得费用v分配到h和l中的最大价值f(v)。也即
f(v)=max{h(k)+l(v-k)|0<=k<=v}
能够看到,f也是一个由泛化物品h和l决定的定义域为0..V的函数,也就是说,f是一个由泛化物品h和l决定的泛化物品。
由此能够定义泛化物品的和:h、l都是泛化物品,若泛化物品f知足以上关系式,则称f是h与l的和。这个运算的时间复杂度取决于背包的容量,是O(V^2)。
泛化物品的定义代表:在一个背包问题中,若将两个泛化物品代以它们的和,不影响问题的答案。事实上,对于其中的物品都是泛化物品的背包问题,求它的答案的过程也就是求全部这些泛化物品之和的过程。设此和为s,则答案就是s[0..V]中的最大值。
背包问题的泛化物品
一个背包问题中,可能会给出不少条件,包括每种物品的费用、价值等属性,物品之间的分组、依赖等关系等。但确定能将问题对应于某个泛化物品。也就是说,给定了全部条件之后,就能够对每一个非负整数v求得:若背包容量为v,将物品装入背包可获得的最大价值是多少,这能够认为是定义在非负整数集上的一件泛化物品。这个泛化物品——或者说问题所对应的一个定义域为非负整数的函数——包含了关于问题自己的高度浓缩的信息。通常而言,求得这个泛化物品的一个子域(例如0..V)的值以后,就能够根据这个函数的取值获得背包问题的最终答案。
综上所述,通常而言,求解背包问题,即求解这个问题所对应的一个函数,即该问题的泛化物品。而求解某个泛化物品的一种方法就是将它表示为若干泛化物品的和而后求之。(以上内容取自背包九讲)
上图取自国家集训队论文《浅谈几类背包题》,点击图片能够放大.
题目意思是想选择一门课,必需要先学会它的必修课,即选择一门课必需要选择必修课。那么他又说要选择的价值最大,这就是背包问题啦。又由于他有依赖性性行为,因此就是个树形dp
解释一下样例:
7 4 2 2 0 1 0 4 2 1 7 1 7 6 2 2
很明显此题的关联关系一目了然,因此咱们能够在子树上适用范化背包.
注意爸爸是0说明没有爸爸
即:
设f[i][j]表示选择以i为根的子树中j个节点。
u表明当前根节点,sum表明其选择的节点的总额。
#include<iostream> #include<cstdio> #include<algorithm> #include<cmath> #include<queue> #include<stack> #include<vector> #include<map> #include<string> #include<cstring> #define ll long long int #define MAXN 2500 using namespace std; inline int read() { char c = getchar(); int x = 0, f = 1; while(c < '0' || c > '9') { if(c == '-') f = -1; c = getchar(); } while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar(); return x * f; } struct node { int next,to; } e[MAXN]; int n,m,head[MAXN],c[MAXN],f[MAXN][MAXN],sum; void add(int x,int y) { e[++sum].next=head[x]; e[sum].to=y; head[x]=sum; } void build(int u,int t) { if(t<=0) return; for(int i=head[u];i;i=e[i].next) { int v=e[i].to; for(int j=0;j<t;++j) { f[v][j]=f[u][j]+c[v]; } build(v,t-1); for(int k=1;k<=t;++k) { f[u][k]=max(f[u][k],f[v][k-1]); } } } int main() { n=read(),m=read(); for(int i=1; i<=n; ++i) { int a; cin>>a>>c[i]; if(a!=0) { add(a,i); } if(a==0) { add(0,i); } } build(0,m); cout<<f[0][m]; return 0; }
这道题比上面的简单多了,咕咕咕,就是直接用第一个dp方程
#include<iostream> #include<cstdio> #include<algorithm> #include<cmath> #include<queue> #include<stack> #include<vector> #include<map> #include<string> #include<cstring> #define ll long long int #define MAXN 6666 using namespace std; const int maxn=999999999; const int minn=-999999999; inline int read() { char c = getchar(); int x = 0, f = 1; while(c < '0' || c > '9') { if(c == '-') f = -1; c = getchar(); } while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar(); return x * f; } /* 注释掉的有bug struct node { int next,to; int in; } e[MAXN]; int n,m,happy[MAXN],head[MAXN],dp[MAXN][MAXN],root,sum; void add(int x,int y) { e[++sum].next=head[x]; e[sum].to=y; head[x]=sum; } void do_it(int x) { for(int i=head[x]; i; i=e[i].next) { do_it(i); dp[x][1]=max(max(dp[x][1],dp[x][1]+dp[i][0]),dp[i][0]); dp[x][0]=max(max(dp[x][0],dp[i][1]+dp[x][0]),max(dp[i][1],dp[i][0])); } } int main() { n=read(); for(int i=1; i<=n; ++i ) happy[i]=read(); for(int i=1; i<=n-1; ++i) { int x,y; cin>>x>>y; e[x].in++; add(y,x); } int zero1,zero2;//读掉0 cin>>zero1>>zero2; for(int i=1; i<=n; ++i) { if(!e[i].in) { root=i; break; } } do_it(root); cout<<max(dp[root][0],dp[root][1]); return 0; } 注释掉的有bug */ int ind[MAXN],n,hap[MAXN],dp[MAXN][2],fa[MAXN],root,vis[MAXN],next[MAXN],po[MAXN]; void do_it(int x) { for(int i = po[x]; i; i = next[i]) { do_it(i); dp[x][1]=max(max(dp[x][1],dp[x][1]+dp[i][0]),dp[i][0]); dp[x][0]=max(max(dp[x][0],dp[i][1]+dp[x][0]),max(dp[i][1],dp [i][0])); } } int main() { cin>>n; for(int i=1; i<=n; i++) cin>>dp[i][1]; for(int i=1; i<=n; i++) { int a,b; cin>>b>>a; ind[b]++; next[b]=po[a]; po[a]=b; } for(int i=1; i<=n; i++) if(!ind[i]) { root=i; break; } do_it(root); cout<<max(dp[root][0],dp[root][1]); }
这篇博客是我学习了网上的大佬和国家队徐持衡的论文后写的,奈何个人水平过低没法深刻的学会,只是肤浅的懂了因此可能会有一些错误,若是发现请评论区告诉我!