题解:人品问题

题解:人品问题*工作~

题目链接

题意概述:

给定一棵树,除根外每个点有一个权值,求选 $k$ 个点后权值最大和,要求一个点被选,那么他的祖先必须被选.

思路:

简单 $n^3$ 次方 $dp$ 一下,n<=100怎末跑都能过,从根节点向下深搜一边,对于一个点 $x$,枚举他的每个儿子,总共选几个点和当前儿子选几个点.

注意:

  • 枚举总共选几个点时,必须先选自己(除根结点不能选自己),并且选取个数不可超过当前节点总个数 $siz[x]$.
  • 在枚举当前儿子选几个点时,必须保证 $p-z>=siz[x]-siz[y]$ ,即剩下p-z个数要选够.选的个数还要小于当前儿子拥有节点个数(不加也没关系,会判成负无穷).
重点:

在枚举选几个儿子时需倒序枚举,不然一个权值较大的节点会被计算多次.类似于01背包变成了完全背包.(我刚开始无脑码的时候就在这儿掉坑了题解:人品问题)

code

#include<bits/stdc++.h>
using namespace std;
const int N=505;
int n,k;
int c[N];
int fr[N],to[N],nxt[N],too=0;
int f[N][N];
int dep[N],siz[N];
void add(int x,int y)
{
    if(!y) return;
    to[++too]=y;
    nxt[too]=fr[x];
    fr[x]=too;
}
void dfs(int x)
{
    f[x][1]=c[x];
    siz[x]=1;
    for(int i=fr[x];i;i=nxt[i])
    {
        int y=to[i];
        dfs(y);
        siz[x]+=siz[y];
        for(int p=min(k,siz[x]);p>=2;p--)
        for(int z=max(1,p+siz[y]-siz[x]);z<=p-1;z++)
        f[x][p]=max(f[x][p],f[x][p-z]+f[y][z]);
    }
}
int main()
{
    cin>>n>>k;
    memset(f,-0x3f,sizeof(f));
    for(int i=2;i<=n;i++)
    scanf("%d",&c[i]);
    for(int i=1;i<=n;i++)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        add(i,x);add(i,y);
    }
    siz[1]=0;
    f[1][0]=0;
    for(int i=fr[1];i;i=nxt[i])
    {
        int y=to[i];
        dfs(y);
        siz[1]+=siz[y];
        for(int p=min(k,siz[1]);p>=1;p--)
        for(int z=max(1,p+siz[y]-siz[1]);z<=p;z++)
        f[1][p]=max(f[1][p],f[1][p-z]+f[y][z]);
    }
    cout<<f[1][k]<<endl;
}
上一篇:洛谷P3469 Solution


下一篇:Gym - 101908L Subway Lines-树剖