码迷,mamicode.com
首页 > 移动开发 > 详细

Luogu-P2015 二叉苹果树

时间:2018-11-17 21:03:23      阅读:198      评论:0      收藏:0      [点我收藏+]

标签:ble   严格   pos   stream   记忆化搜索   P20   枚举   display   节点   

题面传送门: Luogu-P2015

题目描述

给定一棵 \(n\) 个节点的以 \(1\) 为根的二叉树(严格二叉), 树边有边权. 现在需要剪去一些树边(剪边定义为: 若剪去一条边 \((u,v)\), 在删除该边的同时也必须舍弃以 \(v\) 为根的整个子树), 问在留下 \(m\) 条边时边权之和的最大值.

\(n \le 100,m<n\).

题目分析

非常明显的树形DP.

\(F[u][j]\) 表示以 \(u\) 为根的子树中留下 \(j\) 条边的最大权值, 答案即为 \(F[1][m]\).

1.状态转移方程:

\[ F[u][j]=max\{F[u][j-k-1]+F[v][k]+w_{u,v}\}\1 \le j \le min(m,size[u]),0 \le k \le min(j-1,size[v]) \]
其中 \(v\)\(u\) 的一个子节点, \(w_{u,v}\) 表示边 \((u,v)\) 的权值, \(size[i]\) 表以 \(i\) 为根的子树中边的数量, \(k\) 是枚举出来的, 表示在以 \(v\) 为根的子树中选 \(k\) 条边.

2.解释:

  • 若在子树 \(v\) 中选中了 \(k\) 条边, 那么在其他子树中只能选择 \(k-j-1\) 条边(因为当前这条边 \((u,v)\) 必须选择, 不然不合法)
  • \(k\) 的范围必须小于等于 \(j-1\) , 因为当 \(k=j\) 时, \(j-k-1\) 的值为 \(-1\), 显然不合法.

3.复杂度:

因每个点只会访问一次, 每次访问枚举需要 \(n^2\) 的复杂度, 故总复杂度为 \(O(n^3)\), 并且常数极小.

实现细节

  • 记忆化搜索.
  • \(j,k\) 需要倒序枚举, 原因同 \(01\) 背包.
  • 记忆化搜索的同时处理 \(size\) 数组.

代码

/**********************************************************
 * Author        : EndSaH
 * Email         : 2499808100@qq.com
 * Created Time  : 2018-11-17 09:36
 * FileName      : temp.cpp
 * *******************************************************/

#include <cstdio>
#include <cctype>
#include <iostream>

namespace Fast_IO
{/*{{{*/
    char ibuf[1 << 20], obuf[1 << 20], stk[20];
    char *ipos = ibuf, *iend = ibuf, *opos = obuf, *oend = obuf + (1 << 20), *stkpos = stk;

    inline char Getchar()
    {
        return ipos == iend and (iend = (ipos = ibuf) + fread(ibuf, 1, 1 << 20, stdin), ipos == iend) ? EOF : *ipos++;
    }

    inline void Putchar(char c)
    {
        if(opos == oend)
        {
            fwrite(obuf, 1, 1 << 20, stdout);
            opos = obuf;
        }
        *opos++ = c;
    }

    inline int read()
    {
        register int num = 0;
        register bool flag = false;
        register char c;
        while(!isdigit(c = Getchar()))
            flag |= c == '-';
        while(num = (num << 3) + (num << 1) + (c ^ 48), isdigit(c = Getchar()));
        return flag ? -num : num;
    }

    inline void write(int x)
    {
        if(x < 0)
            Putchar('-'), x = -x;
        do
        {
            *stkpos++ = x % 10 ^ 48;
            x /= 10;
        } while(x);
        while(stkpos-- != stk)
            Putchar(*stkpos);
        ++stkpos;
    }
}/*}}}*/
using namespace Fast_IO;

const int maxN = 102;

int n, q, x, y, z, cnt;
int head[maxN], F[maxN][maxN], size[maxN];
bool vis[maxN];

struct Chain
{
    int v, w, next;
} chain[maxN << 1];

template<typename _Tp>
inline bool chkmin(_Tp& x, const _Tp& y)
{/*{{{*/
    return x > y ? (x = y, true) : false;
}/*}}}*/

template<typename _Tp>
inline bool chkmax(_Tp& x, const _Tp& y)
{/*{{{*/
    return x < y ? (x = y, true) : false;
}/*}}}*/


inline void Link(int u, int v, int w)
{
    chain[++cnt] = (Chain){v, w, head[u]};
    head[u] = cnt;
}

void DFS(int u)
{
    vis[u] = true;
    for(register int i = head[u]; i; i = chain[i].next)
    {
        int v = chain[i].v;
        if(vis[v])
            continue;
        DFS(v);
        size[u] += size[v] + 1;
        for(register int j = std::min(q, size[u]); j; --j)
            for(register int k = std::min(j - 1, size[v]); ~k; --k)
                chkmax(F[u][j], F[u][j - k - 1] + F[v][k] + chain[i].w);
    }
}

int main()
{ 
#ifndef ONLINE_JUDGE
    freopen("temp.in", "r", stdin);
    freopen("temp.out", "w", stdout);
#endif

    n = read(), q = read();
    for(register int i = 1; i < n; ++i)
    {
        x = read(), y = read(), z = read();
        Link(x, y, z);
        Link(y, x, z);
    }
    DFS(1);
    write(F[1][q]);

    fwrite(obuf, 1, opos - obuf, stdout);

    return 0;
}

Luogu-P2015 二叉苹果树

标签:ble   严格   pos   stream   记忆化搜索   P20   枚举   display   节点   

原文地址:https://www.cnblogs.com/EndSaH/p/9975180.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!