没有上司的晚会

dp:

题目:

Description

有个公司要举行一场晚会。为了让到会的每个人不受他的直接上司约束而能玩得开心,公司领导决定:如果邀请了某个人,那么一定不会再邀请他的直接的上司,但该人的上司的上司,上司的上司的上司……都可以邀请。已知每个人最多有唯一的一个上司。

已知公司的每个人参加晚会都能为晚会增添一些气氛,求一个邀请方案,使气氛值的和最大。
Input

第1行一个整数N表示公司的人数。

接下一行N个整数。第i行的书表示第i个人的气氛值x(-128<=x<=127)

接N-1下来每行两个整数K,L。表示第K个人是第L个人的上司。

Output

一个数,最大的气氛值和。

Sample Input

4
1 7 3 4
1 2
2 3
2 4
Sample Output

8
Hint

你可以认为公司只有唯一的总boss,这个公司的关系图是一颗树。数据保证最优情况只至少会邀请一个人。

对于20%的数据,n的范围[1,20];

对于40%的数据,n的范围[1,200];

对于60%的数据,n的范围[1,2000];

对于80%的数据,n的范围[1,20000];

对于100%的数据,n的范围[1,200000];

题解:

由题意,我们可以画出如下的关系图(树):
没有上司的晚会_第1张图片
题目里都说这个公司的关系图是一棵树,那么想到树形dp应该就比较容易了吧.
因此,我们关键就是要考虑dp怎么定义转移.而且感觉这题要比选课简单.

题目中的状态及dp的定义:

每个人的活跃值,每个人的直接上司,邀请这个人还是不邀请,某个人邀请与否的最大活跃值.
那么,每个人的直接上司显然使用来造树的,就不用考虑放到dp中的.
每个人的活跃值是放在计算中使用的,也显然不用考虑.
某个人邀请与否的最大活跃值显然是dp的权值,且dp是在树上进行的,那么活跃度应该指的是这个人及这个人的子树所能达到的最大活跃度.
这样只用考虑dp的下标是什么就可以了,且已经没剩下多少状态了.
根据转移,可以很快的想到下标要存某一个人,以及这个人是否被邀请.
这样dp的状态就定义出来了.
dp[i][j] i=0时表示j不取,i=1时表示j取,dp[0][j]表示j不取时的(j及j的子树的)最大活跃度,dp[1][j]表示j取时的(j及j的子树的)最大活跃度

dp的转移:

根据题目的描述以及之前dp状态的定义,这个转移就是取与不取的问题了.
如果i这个人取了,那么他(她)的下属就不能再取了,但是他(她)下属的下属还是可以被取的,这样就只用考虑他(她)的下属不被取的情况了.
如果这个人不取,那么他(她)的下属就可以取,这是就要考虑他(她)的下属是取了后活跃值比较大,还是不取活跃值比较大.
这样我么就把一个大的问题转化为一个子问题,只要考虑一个人,那么就可以得到答案了.
递推式:dp[0][i]=max(dp[0][son[i][1]],dp[1][son[i][1]])+···+max(dp[0][son[i][k]],dp[1][son[i][k]]);(k∈[1,i的下属的总数])(当没有下属时自然就不会进行转移了)
dp[1][i]=dp[0][son[i][1]]+···+dp[0][son[i][k]];(k∈[1,i的下属的总数])(当i没有下属时自然就不会进行转移了)

代码如下:

#include
#include
#include
#include
#define M 200005
using namespace std;
int val[M];
vector<int>G[M];
int n,Boss;
int dp[2][M];
bool mark[M];
int dfs(int x,int f){//f=0表示不取,f=1表示取 
    if(dp[f][x])return dp[f][x];
    int pre=0;
    if(f==1){
        pre+=val[x];
        for(int i=0;iint y=G[x][i];
            pre+=dfs(y,0);
        }
        return dp[f][x]=pre;
    }else{
        for(int i=0;iint y=G[x][i];
            pre+=max(dfs(y,1),dfs(y,0));
        }
        return dp[f][x]=pre;
    }
}
int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        scanf("%d",&val[i]);
    }
    for(int i=1;iint a,b;
        scanf("%d %d",&a,&b);
        G[a].push_back(b);
        mark[b]=1;
    }
    //把公司的总boss找出来,当做整棵树的根
    for(int i=1;i<=n;i++){
        if(mark[i]==0){
            Boss=i;
            break;
        }
    }
    printf("%d\n",max(dfs(Boss,1),dfs(Boss,0)));
    return 0;
}

这样就能求出最大活跃值了.
希望你们能好好学习,
今天初赛考不好了(;′⌒`)(都是那万恶的’, ‘啊),
不能再经常写题解了/(ㄒoㄒ)/~~.

你可能感兴趣的:(没有上司的晚会)