bzoj4813: [Cqoi2017]小Q的棋盘

链接

  http://www.lydsy.com/JudgeOnline/problem.php?id=4813

题解

  这题细节很多,需要仔细考虑下。
  用 f[i][j] 表示以 i 为根的子树,走j步最后回到 i 节点,最多能到达的节点数。
  g[i][j]则表示以 i 为根的子树,走j步,不必回到 i 节点,最多能到达的节点数。
  我们边dfs边做 dp ,考虑加入一棵子树会对之前的信息造成什么影响。
  首先新加节点的 f 可以更新之前的f,可以枚举给子树分配多少步,给之前的点分配多少步,然后转移。这个地方应该不能直接用 f 数组转移f数组,这样可能出错,应该先把 f 数组copy一遍,用副本进行转移。
  新加节点的 g 可以更新之前的g,这一部分答案的意义是最后结束在当前子树内。
  新加节点的 f 也可以更新之前的g,这一部分答案的意义是最后结束在之前的子树内。
  对于两次对 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;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值