树形DP
知识要点
树形dp就是在“树”的数据结构上的dp ,用递推(叶子到根,根到叶子。)的方法,使得在更小规模的时候,子集容易找。难点:转移。(父亲,兄弟,子树)
常用方法
1.换根操作
2.链上统计
例题
1. 依赖背包
先对树进行DFS,得到DFS序。
mx[i]表示i这个节点及其子树中,DFS序最大的值
dp[i][j]表示第i个点及以后的dfs序构成的泛化物品,体积大小为j的最大权值。
则dp[ i ][ j ] = max( dp[ mx[ i]+1 ][ j ],dp[ i+1 ][ j-w[ i ] ]+v[ i ] )
2. codeforces 219 D
这道题还是很简单的。首先可以很轻易的求出对于一个点的对应值(dp[u]=sum(dp[son[u]]+<son[u],u>))
而之后只要进行换根操作,每次考虑一条边就行了。
3. poj 3170
。。。裸的重心
dp[i]表示i这个子树内,节点个数
ans=min(max(n-dp[i],sum[dp[son[i]]]));
4. (无题号)给你一颗树,每个节点都有权值,有些节点也是陷阱。现在从树上一点出发,不经过重复的节点,到另一个节点终止,并且经过最大不超过C个陷阱(C<4),要求路径的权值和最大。
dp[i][j]表示从i出发,向下,经过j个陷阱的最大权值。
dp[i][j]=max(dp[son[i]][j-[i是陷阱]]+val[i])
然后换根操作或者链上统计(更简单)即可
5. Hdu 4980
对于一个边统计,向上经过的次数一定<=2(否则可以合并两条从而达到更好的效果),那么dp[i][j]
表示i这个节点的子树的边全部访问完全,并且有j条向上的队员。
粘代码:
for(int i=0;i<=2;i++)dp[u][i]=i*k;
for(int i=0;i<=2;i++)temp[i]=INF;
temp[0]=min(dp[u][1]+dp[v][1]-k+e[ss].w,dp[u][2]+dp[v][2]-2*k+2*e[ss].w);
temp[1]=min(dp[u][0]+dp[v][1]+e[ss].w,dp[u][1]+dp[v][2]-k+2*e[ss].w);
temp[1]=min(temp[1],dp[u][2]+dp[v][1]-k+e[ss].w); temp[2]=min(dp[u][0]+dp[v][2]+2*e[ss].w,dp[u][1]+dp[v][1]+e[ss].w);
temp[2]=min(temp[2],dp[v][2]+dp[u][2]-k+2*e[ss].w);
for(int i=0;i<=2;i++)dp[u][i]=tmp[i];