zoukankan      html  css  js  c++  java
  • 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;
    }
    
    
  • 相关阅读:
    zoj 2316 Matrix Multiplication 解题报告
    BestCoder7 1001 Little Pony and Permutation(hdu 4985) 解题报告
    codeforces 463C. Gargari and Bishops 解题报告
    codeforces 463B Caisa and Pylons 解题报告
    codeforces 463A Caisa and Sugar 解题报告
    CSS3新的字体尺寸单位rem
    CSS中文字体对照表
    引用外部CSS的link和import方式的分析与比较
    CSS样式表引用方式
    10个CSS简写/优化技巧
  • 原文地址:https://www.cnblogs.com/DarkValkyrie/p/11143932.html
Copyright © 2011-2022 走看看