$Luogu$ $P2015$ 二叉苹果树

链接

背景

\(Luogu\) \(P1122/Codevs5565\)

题意

给定一棵 \(n\) 个节点的以 \(1\) 号点为根节点的树的 \(n-1\) 条边及权值 \((x_i,y_i,w_i)\) ,求砍掉一些枝条后留下 \(q\) 条边的最大边权和。

解法

也不是树形 \(dp\) 模板。还是习题。
由于这棵树是二叉树,有一个优美的性质:某棵子树内保留的树枝只能由左右子树得到(这不是废话吗)。
考虑转换题意,砍掉一些枝条后留下 \(q\) 条边也即留下 \(q+1\) 个点且必须包含根。
于是就可以设 \(f_{x,i}\) 表示以 \(x\) 节点为根的子树留下 \(i\) 个点的最大边权和。
这个时候就要考虑继续转换题目了。由于根必选,所以 \(n-1\) 条边完全可以转化成剩下 \(n-1\) 个点的权值(每个点到它父亲的边的权值)。于是把最大边权和变成了最大点权和。
然后就可以高高兴兴转移了:设节点 \(x\) 的左儿子是 \(l_x\) ,右儿子是 \(r_x\) ,则 \(f_{x,i}=\max_\limits{j \in [0,i-1]} \{ f_{l_x,j}+f_{r_x,i-1-j} \} +val_x\)
至于 \(l_x\)\(r_x\) 的求法,见下面的 \(trick\) 吧。

trick

一棵给定 \(n-1\) 条无向边的 \(n(n \leqslant 3 \times 10^3)\) 点有根二叉树左右子节点的求法:
建立邻接矩阵,只不过这次要存成 \(int\) 形式, \(g_{x,y}\) 存的是 \((x,y)\) 的边权。
从根开始建立,自顶向下递归。
每次递归到当前节点 \(x\) 时都遍历 \(n\) 个节点,找到第一个有边权的节点,将其作为 \(l_x\) ,然后递归处理该节点。返回后直接退出循环即可。找 \(r_x\) 时注意要重新遍历 \(n\) 个节点,找到第一个有边权的节点,将其作为 \(r_x\) ,然后递归处理该节点。

细节

注意两个边界: \(f_{x,0}\) 表示选 \(0\) 个节点,答案必然为 \(0\)\(l_x=0\)\(r_x=0\) (即 \(x\) 是个叶节点)时没法选子树,直接返回本身权值即可。

代码

\(View\) \(Code\)

#include<bits/stdc++.h>
using namespace std;
inline int read()
{
    int ret=0,f=1;
    char ch=getchar();
    while('9'<ch||ch<'0')
    {
        if(ch=='-')
            f=-1;
        ch=getchar();
    }
    while('0'<=ch&&ch<='9')
    {
        ret=(ret<<1)+(ret<<3)+ch-'0';
        ch=getchar();
    }
    return ret*f;
}
int n,q,x,y,w,g[105][105],val[105],l[105],r[105],f[105][105];
void make_tree(int x)
{
    for(register int i=1;i<=n;i++)
    {
        if(g[x][i]||!g[x][i])
        {
            val[i]=g[x][i];
            g[x][i]=-1;
            g[i][x]=-1;
            l[x]=i;
            make_tree(i);
            break;
        }
    }
    for(register int i=1;i<=n;i++)
    {
        if(g[x][i]||!g[x][i])
        {
            val[i]=g[x][i];
            g[x][i]=-1;
            g[i][x]=-1;
            r[x]=i;
            make_tree(i);
            break;
        }
    }
}
int dp(int x,int cnt)
{
    if(!cnt)
        return 0;
    if((!l[x])&&(!r[x]))
        return val[x];
    if(f[x][cnt])
        return f[x][cnt];
    for(register int i=0;i<cnt;i++)
    {
        f[x][cnt]=max(f[x][cnt],dp(l[x],i)+dp(r[x],cnt-1-i)+val[x]);
    }
    return f[x][cnt];
}
int main()
{
    n=read();
    q=read();
    memset(g,-1,sizeof(g));
    for(register int i=1;i<n;i++)
    {
        x=read();
        y=read();
        w=read();
        g[x][y]=w;
        g[y][x]=w;
    }
    make_tree(1);
    printf("%d\n",dp(1,q+1));
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/Peter0701/p/11838826.html