题解【没有上司的舞会】

期末考前写题解,\(rp++\)
\[ description \]
给出一棵点带权树,你需要选取若干个点:

\(<1> :\) 选取的点两两不具有父子关系

\(<2> :\) 在满足 \(<1>\) 的情况下最大化点权和

求最大的点权和。
\[ solution \]
树形 \(dp\) 经典。

f[u][0] 表示在不选 \(u\) 的情况下,在以 \(u\) 为根的子树中取点的最大点权和。

f[u][1] 表示在选取 \(u\) 的情况下,在以 \(u\) 为根的子树中取点的最大点权和。

若不选 \(u\) ,则对于 \(u\) 的任意一个儿子,取不取都不会与 \(u\) 形成父子关系,则有状态转移方程:
\[ f[u][0]=\sum_{v=\operatorname{son}(u)}\operatorname{max}(f[v][0],f[v][1]) \]
若选取 \(u\) ,则不能选取 \(u\) 的任意一个儿子,则有状态转移方程:
\[ f[u][1]=val[u]+\sum_{v=\operatorname{son}(u)}f[v][0] \]
答案为 \(\operatorname{max}(f[root][0],f[root][1])\)

一次树形 \(dp\) 即可解决。
\[ code \]

#include<cstdio>
#include<algorithm>

#define RI register int

using namespace std;

inline int read()
{
    int x=0,f=1;char s=getchar();
    while(s<'0'||s>'9'){if(s=='-')f=-f;s=getchar();}
    while(s>='0'&&s<='9'){x=x*10+s-'0';s=getchar();}
    return x*f;
}

const int N=6100,M=6100;

int n;

int val[N];

bool sig[N];

int tot,head[N],ver[M],Next[M];

void add(int u,int v)
{
    ver[++tot]=v;    Next[tot]=head[u];    head[u]=tot; 
}

int f[N][2];

void dp(int u)
{
    f[u][0]=0;
    f[u][1]=val[u];
    for(RI i=head[u];i;i=Next[i])
    {
        int v=ver[i];
        dp(v);
        f[u][0]+=max(f[v][0],f[v][1]);
        f[u][1]+=f[v][0];
    }
}

int main()
{
    n=read();

    for(RI i=1;i<=n;i++)
        val[i]=read();

    for(RI i=1;i<n;i++)
    {
        int u=read(),fa=read();
        sig[u]=true,add(fa,u);
    }

    read(),read();

    int root;
    for(RI i=1;i<=n;i++)
    if(sig[i]==false)
    {
        root=i;
        break;
    }

    dp(root);

    printf("%d\n",max(f[root][0],f[root][1]));

    return 0;
}

\[ thanks \ for \ watching \]

猜你喜欢

转载自www.cnblogs.com/cjtcalc/p/12216599.html
今日推荐