树形dp练习
题型1:树形背包 P2014
题解
类似于有依赖的背包问题,不过依赖关系更为复杂
我们将依赖关系化为一棵树(本题需要建一个虚拟节点0),然后递归求解子树,然后再做dp
代码
#include<bits/stdc++.h>
#define M 509
using namespace std;
int read(){
int f=1,re=0;
char ch;
for(ch=getchar();!isdigit(ch)&&ch!='-';ch=getchar());
if(ch=='-'){f=-1,ch=getchar();}
for(;isdigit(ch);ch=getchar()) re=(re<<3)+(re<<1)+ch-'0';
return re*f;
}
int tot,f[M][M],nxt[M],to[M],first[M],a[M],n,m;
void add(int x,int y){
nxt[++tot]=first[x];
first[x]=tot;
to[tot]=y;
}
void dfs(int u){
f[u][0]=0;
for(int i=first[u];i;i=nxt[i]){
int v=to[i];
dfs(v);
for(int j=m;j>=0;j--)
for(int k=j;k>=0;k--)//这种状态转移方程在树形背包中很常见
f[u][j]=max(f[u][j],f[u][j-k]+f[v][k]);
}if(u) for(int i=m;i>=1;i--) f[u][i]=f[u][i-1]+a[u];
}
int main(){
n=read(),m=read();
for(int i=1;i<=n;i++){
int x=read();a[i]=read();
add(x,i);
}dfs(0);printf("%d\n",f[0][m]);
return 0;
}
题型2:二次扫描与换根法 POJ3585
题解
显然,最为暴力的方法就是枚举每一个点作为源点的情况,然后再对其进行树形dp,但显然时间复杂度爆炸,因此我们考虑,其实我们暴力的方法中重复求解了很多东西。
因此我们只需要两次树形dp,首先随便找个点作为源点,然后做一次树形dp,然后再从这点树形dp一次,根据上次求出的结果来推出每个作为源点的答案。详细代码实现如下。
代码
#include <cmath>//换根法
#include <cstdio>
#include <climits>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#define M 1000009
using namespace std;
int read(){
int f=1,re=0;
char ch;
for(ch=getchar();!isdigit(ch)&&ch!='-';ch=getchar());
if(ch=='-'){f=-1,ch=getchar();}
for(;isdigit(ch);ch=getchar()) re=(re<<3)+(re<<1)+ch-'0';
return re*f;
}
int tot,nxt[M],first[M],to[M],w[M],g[M],f[M],rd[M],n,ans,t;
void add(int x,int y,int z){
nxt[++tot]=first[x];
first[x]=tot;
to[tot]=y;
w[tot]=z;
}
void dfs1(int u,int fa){
for(int i=first[u];i;i=nxt[i]){
int v=to[i];
if(v==fa) continue;
dfs1(v,u);
if(rd[v]==1) g[u]+=w[i];
else g[u]+=min(g[v],w[i]);
}
}
void dfs2(int u,int fa){
for(int i=first[u];i;i=nxt[i]){
int v=to[i];
if(v==fa) continue;
if(rd[u]==1) f[v]=g[v]+w[i];
else f[v]=g[v]+min(f[u]-min(g[v],w[i]),w[i]);
ans=max(f[v],ans),dfs2(v,u);
}
}
int main(){
t=read();
while(t--){
n=read();ans=tot=0;
memset(rd,0,sizeof(rd));
memset(f,0,sizeof(f));
memset(g,0,sizeof(g));
memset(first,0,sizeof(first));
for(int i=1;i<n;i++){
int x=read(),y=read(),z=read();
add(x,y,z),add(y,x,z),rd[x]++,rd[y]++;
}dfs1(1,0),ans=f[1]=g[1],dfs2(1,0);
printf("%d\n",ans);
}return 0;
}
小结
换根法更多是一种思想,如何根据第一次的结果推导出其他情况,这也要因题而异
本文深入讲解树形DP的两种典型应用:树形背包问题和换根法。通过具体题目解析,如P2014和POJ3585,阐述了如何利用树形结构优化动态规划过程,减少重复计算,提高效率。
1113

被折叠的 条评论
为什么被折叠?



