树形DP
通常,我们从根节点出发,向子节点做深度优先搜索,并由其子节点的最优解合并得到该节点的最优解。
有些问题,我们还需再次从根节点出发,向子节点做深度优先搜索,对于树上的每个节点(除根节点外)
由父节点的信息(父节点合并后的信息,除去该孩子的信息,就是其与孩子的信息)更新该节点的信息
【简单树形 DP】
#include<bits/stdc++.h>
using namespace std;
const int N=6010;
int n,fir[N],nex[N],v[N],dp[N][2],w[N],idx,root;
bool st[N];
void add(int x,int y)
{
v[idx]=y;
nex[idx]=fir[x];
fir[x]=idx++;
}
void dfs(int x)
{
dp[x][1]=w[x]; //初始化来的时候
for(int i=fir[x];~i;i=nex[i])
{
int j=v[i];
dfs(j); //状态方程
dp[x][0]+=max(dp[j][0],dp[j][1]);//上司不来下属可以来也可以不来
dp[x][1]+=dp[j][0]; //上司来下属就不可以来
}
}
int main()
{
ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
memset(fir,-1,sizeof fir);
cin>>n;
for(int i=1;i<=n;i++)
cin>>w[i];
for(int i=1;i<n;i++)
{
int x,y;
cin>>x>>y;
add(y,x); //反向存边 后面的是上司
st[x]=true;
}
for(int i=1;i<=n;i++)//找根节点
{
if(st[i]==false)
{
root=i;
break;
}
}
dfs(root); //从根节点开始遍历
cout<<max(dp[root][0],dp[root][1]);
return 0;
}
【树形 DP + 背包】
#include<bits/stdc++.h>
using namespace std;
const int N=105;
int n,m,dp[N][N],w[N][N],vis[N];
vector<int> a[N];
void dfs(int x)
{
vis[x]=1; //父节点
for(int i=0;i<a[x].size();i++)
{
int u=a[x][i];
if(vis[u])
continue;
dfs(u);
for(int j=m;j>=1;j--) //背包
{
for(int k=j-1;k>=0;k--)
{
dp[x][j]=max(dp[x][j],dp[u][k]+dp[x][j-k-1]+w[x][u]);
//以x为根节点选取j个树枝 = x的下一个节点选k个树枝 + 它的
//兄弟节点选剩下的(j-k-1)个树枝 + x到u的苹果数
}
}
}
}
int main()
{
cin>>n>>m;
for(int i=1;i<n;i++)
{
int x,y,z;
cin>>x>>y>>z;
w[x][y]=z,w[y][x]=z;
a[x].push_back(y);
a[y].push_back(x);
}
dfs(1);
cout<<dp[1][m];
return 0;
}
【换根 DP】
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N=2000005;
LL n,m,t,maxn,dp[N],v[N],fir[N],nex[N],f[N],idx,size[N];
//size[x]:整棵树以1为根节点时,以x为根的子树的节点数量
//f[x]:整棵树以1为根节点时,以x为根的子树的所有节点的深度之和
//dp[x]:整棵树以x为根时所有节点的深度之和
void add(int x,int y)
{
v[idx]=y;
nex[idx]=fir[x];
fir[x]=idx++;
}
void dfs1(int x,int y)
{
f[x]=0;
size[x]=1;
for(int i=fir[x];~i;i=nex[i])
{
int j=v[i];
if(j==y)
continue;
dfs1(j,x);
size[x]+=size[j]; //子树结点个数
f[x]+=f[j]+size[j]; //求深度之和
}
}
void dfs2(int x,int y)
{
for(int i=fir[x];~i;i=nex[i])
{
int j=v[i];
if(j==y)
continue;//本来是以x为根的树,变成以儿子j为根的树,
//那么j的所有结点的深度都会减1,深度和就会减少size[j],
//同样地,所有不在j的子树上的结点的深度都会+1,深度和就会加上n - size[j].
dp[j]=dp[x]+n-2*size[j];
dfs2(j,x);
}
}
int main()
{
cin>>n;
memset(fir,-1,sizeof(fir));
for(int i=1;i<n;i++)
{
int x,y;
cin>>x>>y;
add(x,y),add(y,x);
}
dfs1(1,0);
dp[1]=f[1]; //初始化
dfs2(1,0);
for(int i=1;i<=n;i++)
{
if(dp[i]>maxn)
{
maxn=dp[i],t=i;
}
}
cout<<t;
return 0;
}
浙公网安备 33010602011771号