多校训练的树形dp就没有做出来过,所以最近也是网上把树形dp的题扒出来做一遍。
其实跟普通的dp一样,树形dp也是需要找状态,确定状态转移方程,只不过状态转移的过程比较明确,就是从父亲向儿子转移或者儿子向父亲转移,只不过怎么转移通常是很难的问题。。。
这个题的难点在于走向某一个节点之后还可能回来,然而也因此,状态就非常明显了,明显要分两类,一类是经过当前节点后回到当前节点,另一种是经过当前节点后没回来,
所以用f[i][j][0]代表i节点用j步最后回到i点的最大价值,f[i][j][1]代表i节点用j步后不会到i点的最大价值。
至于转移,
f[v][j][0]=max(f[v][j][0],f[v][l][0]+f[u][j-l-2][0]);
因为从u->v和从v->u需要走两步,所以到u点实际能用的步数就是j-l-2了
f[u][j][1]=max(f[u][j][1],f[u][l][1]+f[tmp][j-l-2][0]);
f[u][j][1]=max(f[u][j][1],f[u][l][0]+f[tmp][j-l-1][1]);
f[u][j][1]=max(f[u][j][1],f[u][l][0]+f[tmp][j-l-1][0]);
这个就是不回去的推法了。。。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<algorithm>
#include<vector>
using namespace std;
int w[2010],f[110][210][2],n,k;
vector <int> vec[110];
void dfs(int u,int v)
{
for (int i=1;i<=k;i++)
{
f[u][i][0]=w[u];
f[u][i][1]=w[u];
}
f[u][0][0]=w[u];
for (int i=vec[u].size()-1;i>=0;i--)
{
int tmp=vec[u][i];
if (tmp==v) continue;
dfs(tmp,u);
for (int j=k;j>=1;j--)
for (int l=0;l<=j;l++)
{
if (j-l-2>=0)
{
f[u][j][0]=max(f[u][j][0],f[u][l][0]+f[tmp][j-l-2][0]);
f[u][j][1]=max(f[u][j][1],f[u][l][1]+f[tmp][j-l-2][0]);
}
if (j-l-1>=0)
{
f[u][j][1]=max(f[u][j][1],f[u][l][0]+f[tmp][j-l-1][1]);
f[u][j][1]=max(f[u][j][1],f[u][l][0]+f[tmp][j-l-1][0]);
}
}
}
}
int main()
{
int x,y;
while (scanf("%d%d",&n,&k)!=EOF)
{
memset(f,0,sizeof(f));
memset(w,0,sizeof(w));
for (int i=1;i<=n;i++)
scanf("%d",&w[i]);
for (int i=1;i<n;i++)
{
scanf("%d%d",&x,&y);
vec[x].push_back(y);
vec[y].push_back(x);
}
dfs(1,0);
for (int i=1;i<=n;i++)
vec[i].clear();
printf("%d\n",max(f[1][k][0],f[1][k][1]));
}
return 0;
}