链接
http://www.lydsy.com/JudgeOnline/problem.php?id=4813
题解
这题细节很多,需要仔细考虑下。
用
f[i][j]
表示以
i
为根的子树,走
我们边
首先新加节点的
f
可以更新之前的
新加节点的
g
可以更新之前的
新加节点的
f
也可以更新之前的
对于两次对
g
<script type="math/tex" id="MathJax-Element-2331">g</script>的更新,可能会涉及到顺序问题,如果先做第一种再做第二种,就可能出问题,答案可能包含了最后一棵子树多次走的情况。因此应该先进行第二种,后进行第一种。
代码
//树形dp
#include <cstdio>
#include <algorithm>
#define maxn 300
using namespace std;
int N, M, head[maxn], to[maxn], nex[maxn], tot, f[maxn][maxn], g[maxn][maxn],
tmp[maxn];
void adde(int a, int b){to[++tot]=b;nex[tot]=head[a];head[a]=tot;}
void dp(int pos, int pre)
{
int i, j, p;
f[pos][0]=g[pos][0]=1;
for(p=head[pos];p;p=nex[p])
if(to[p]^pre)
{
dp(to[p],pos);
for(i=0;i<=M;i++)tmp[i]=g[pos][i];
for(i=0;i<=M;i++)
for(j=i+2;j<=M;j++)g[pos][j]=max(g[pos][j],tmp[j-i-2]+f[to[p]][i]);
for(i=0;i<=M;i++)tmp[i]=f[pos][i];
for(i=0;i<=M;i++)
{
for(j=i+2;j<=M;j++)f[pos][j]=max(f[pos][j],tmp[j-i-2]+f[to[p]][i]);
for(j=i+1;j<=M;j++)g[pos][j]=max(g[pos][j],tmp[j-i-1]+g[to[p]][i]);
}
}
}
int main()
{
int i, a, b, ans=-1;
scanf("%d%d",&N,&M);
for(i=1;i<N;i++)scanf("%d%d",&a,&b),adde(a,b),adde(b,a);
dp(0,-1);
for(i=0;i<=M;i++)ans=max(ans,max(f[0][i],g[0][i]));
printf("%d",ans);
return 0;
}