• P1352 没有上司的舞会[树形dp]


    题目描述

    某大学有N个职员,编号为1~N。他们之间有从属关系,也就是说他们的关系就像一棵以校长为根的树,父结点就是子结点的直接上司。现在有个周年庆宴会,宴会每邀请来一个职员都会增加一定的快乐指数Ri,但是呢,如果某个职员的上司来参加舞会了,那么这个职员就无论如何也不肯来参加舞会了。所以,请你编程计算,邀请哪些职员可以使快乐指数最大,求最大的快乐指数。

    输入输出格式

    输入格式:

    第一行一个整数N。(1<=N<=6000)

    接下来N行,第i+1行表示i号职员的快乐指数Ri。(-128<=Ri<=127)

    接下来N-1行,每行输入一对整数L,K。表示K是L的直接上司。

    最后一行输入0 0

    输出格式:

    输出最大的快乐指数。

    输入输出样例

    输入样例#1:

    7
    1
    1
    1
    1
    1
    1
    1
    1 3
    2 3
    6 4
    7 4
    4 5
    3 5
    0 0
    

    输出样例#1:

    5
    

    解析:

    这是一道相当经典的树形dp入门题。


    树形(dp),顾名思义,就是在树这种数据结构上做(dp),所以要学习树形(dp),首先要学习树的储存结构和遍历方法。

    显然,在这道题中,我们可以以树的深度作为阶段,用某个人的以他为根的子树的最优解作为状态,决策就是某个人来与不来。


    那岂不就是直接开一个一维数组(dp[])来做就得了?

    其实不然。

    我们会发现这样做忽略了在做当前决策时,之前做过的决策实际上是会当前决策影响的。

    考虑如下情形:如果一个人的上司来了,那么他只有不来一种选择;如果一个人的上司没来,那么他既可以来也可以不来。然后这个人的决策又会影响到他的下属,继而传播到整颗子树。

    因此,这道题是有后效性的


    不急,对于这种情况,我们再加一维把任意一个人来与不来的情况分开记录,就不会使最优解互相影响了。

    假设(dp[i][1])表示第(i)个人当前如果来的话的最优解,(dp[i][0])就表示第(i)个人不来时的最优解。

    初始化就是对于任意的一个人(i),有(dp[i][0]=0,dp[i][1]=w[i]),其中(w[i])表示这个人的嗑嗨指数。

    状态转移方程:

    [{dp[i][1]+=sum_{jepsilon son(i) }{dp[j][0]}}, {dp[i][0]=sum_{j epsilon son(i)} max(dp[j][0],dp[j][1])} ]

    我们可以(dfs)一遍整棵树,在向下递归时初始化,向上递归时做(dp)

    参考代码:

    #include<cstdio>
    #include<iostream>
    #include<cmath>
    #include<cstring>
    #include<ctime>
    #include<cstdlib>
    #include<algorithm>
    #include<queue>
    #include<set>
    #include<map>
    #define ri register int
    const int N=6010;
    const int INF=0x3f3f3f3f;
    using namespace std;
    inline int read()
    {
        int f=1,x=0;char c=getchar();
        while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
        while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
        return x*f;
    }
    struct rec{
        int next,ver;
    }g[N<<1];
    int head[N],tot,n,w[N];
    int dp[N][2];
    bool v[N],fa[N];
    void add(int x,int y)
    {
        g[++tot].ver=y;
        g[tot].next=head[x],head[x]=tot;
    }
    void calc(int x)
    {
        v[x]=1;
        dp[x][0]=0;
        dp[x][1]=w[x];
        for(ri i=head[x];i;i=g[i].next){
            int y=g[i].ver;
            if(v[y]==1) continue;
            calc(y);
            dp[x][0]+=max(dp[y][1],dp[y][0]);
            dp[x][1]+=dp[y][0];
        }
    }
    int main()
    {
        n=read();
        for(ri i=1;i<=n;i++) w[i]=read();
        int x,y;
        for(ri i=1;i<n;i++){
            x=read(),y=read();
            add(y,x);fa[x]=1;
        }
        getchar();getchar();
        int root;
        for(ri i=1;i<=n;i++){
            if(!fa[i]){
                root=i;
                break;
            }
        }
        calc(root);
        cout<<max(dp[root][1],dp[root][0])<<endl;
        return 0;
    }
    
    
  • 相关阅读:
    java 笔记(6) static关键字
    java 笔记(5)接口,static
    java 笔记(4)抽象类与抽象方法
    java 笔记(3)多态 容易理解
    看穿CSS的定位技术
    揭开浮动布局的秘密(简略版)
    不可不知的CSS盒子模型(简略版)
    初学Java web 项目
    Eclipse快捷键
    JDBC连接数据库
  • 原文地址:https://www.cnblogs.com/DarkValkyrie/p/11143932.html
Copyright © 2020-2023  润新知