没有上司的晚会 弱弱的题解 (话说我终于会树形DP了)

一-观赏题目

注意文明观题,不要内卷(bushi
题目有些地方可能和其他网站的题有所不同,本题解代码以此题面为准
(其实本题代码稍加修改就可以过其他网站的此题)

问题描述

有个公司要举行一场晚会。
为了能玩得开心,公司领导决定:如果邀请了某个人,那么一定不会邀请他的上司
(上司的上司,上司的上司的上司……都可以邀请)。
每个参加晚会的人都能为晚会增添一些气氛,求一个邀请方案,使气氛值的和最大。

输入格式

第1行一个整数N(1\(\le\)N \(\le\) 6000)表示公司的人数。
接下来N行每行一个整数。第i行的数表示第i个人的气氛值x(-128\(\le\)x$\le$127)。
接下来每行两个整数L,K。表示第K个人是第L个人的上司。
输入以0 0结束。

输出格式

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

样例输入

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

样例输出

5

二-题目解析

1.发现一个显而易见的东西

看完题,我们很容易发现由 老板-员工 这样的关系组成的网状结构是一棵树,那我们只要找到老板,对其子树进行遍历就行

2.关键是咋遍历捏

我们可以使用dfs或是bfs,最后本蒟蒻将展示dfs滴做法(绝对不是因为好写)
至于具体怎么做,请看下面

3.那怎么才能找到最优解捏

1.最优解?DP?

想到这样的找最优解,你可能想到了数字三角形这道比较基础的DP题,而且其结构与这道题极其类似,但是……谁说一个老板最多只有两个员工了啊喂 : ( 但我们还是可以用他的思路来解这道题的呢!

2.DP!

既然是DP,肯定少不了以下步骤

a)DP?状态?

既然想要用DP来解一解题,那么肯定要找状态,在这里,本蒟蒻设置一个状态。

我们设\(dp[i][j]\)的值为 第i号的公司成员 在i号节点 处于编号为j的情况 时在 他与他的子树这个集合 能够产生的 最大的happy值。那么显而易见,最终的答案就是

\(max(dp[CEO的编号][0], dp[CEO的编号][1])\)

注:j=0时表示这个编号为i的公司成员不会去,j=1时表示编号为i的公司成员会去

b)状态?转移方程?

有了状态,那转移方程也必不可少。
有了上面的状态呢根据在 二.1 中发现的东西,可以得出:

当编号为i的公司成员不去的时候,他的员工们可去可不去 即
\(dp[i][0]=i号成员的快乐值+\sum_{\max (dp[i的所有子节点][0], dp[i的所有子节点][1])}\)
当编号为i的公司成员要去的时候,他的员工们只能家里蹲 即
\(dp[i][1]=i号成员的快乐值+\sum_{dp[i的所有子节点][0]}\)

这样,方程我们就求出来了!

三-dfs的详细过程

树?建树咋建?

我们可以用链式前向星来存哦

若y是x的上司,则建一条从y指向x的有向边

找根节点!

在建树的时候记录每个节点的入度,建完以后,找那个入度为0的点,尊为CEO(下面记作root)

dfs!

接下来,从root节点开始dfs,执行以下过程:

1.每到达一个点,将初值赋值为这个节点的快乐值
2.通过链式前向星遍历当前节点的每个子节点,并进入子程序
3.每遍历完一个子节点就进行一次状态转移(方程已经有了)
4.结束这个程序

输出!

输出上面我们推出来的答案:\(max(dp[CEO的编号][0], dp[CEO的编号][1])\)

四-思路?代码?

点击查看代码
#define ll long long
ll n, cnt;
ll a[1000015];
ll head[1000015];
ll dp[6001][2];
bool flag[1000015];
ll x, y;
ll rot;
struct edge {//链式前向星
    ll to, next;
} e[1000015];
void add(ll u, ll v) {//加边函数
    e[++cnt].next=head[u];
    head[u]=cnt;
    e[cnt].to=v;
}
void dfs(ll u) {//dfs
    dp[u][0]=0, dp[u][1]=a[u];
    for(ll i=head[u]; i; i=e[i].next) {//链式前向星枚举子节点
        dfs(e[i].to);
        dp[u][0]+=max(dp[e[i].to][1], dp[e[i].to][0]);
        dp[u][1]+=dp[e[i].to][0];
    }
}
int main() {
    scanf("%lld", &n);
    for(ll i=1; i<=n; i++) scanf("%lld", &a[i]);//输入快乐值,其中a[i]表示i号公司成员的快乐值
    do {
        scanf("%lld %lld", &x, &y);
        if(x!=0 and y!=0) {
            add(y, x);
            flag[x]=true;//统计入度
        }
    } while(x!=0 and y!=0);//要判断是否x,y都是0,如果满足的话,表示关系已经输入完毕,需要退出
    for(ll i=1; i<=n; i++) 
        if(!flag[i]) {//判断入度是否为零
            rot=i;//尊为CEO
            break;
        }
    dfs(rot);
    printf("%lld\n", max(dp[rot][0], dp[rot][1]));//输出
    return 0;
}

变式练习

这道题是属于只有一个CEO的题。在某些变式练习中,会出现多个CEO的情况(也就是股东),那么我们可以从一个0号节点,向每个CEO都建一条边,最终的答案就会变成\(max(dp[0][0], dp[0][1])\)

撒花

好了,这道题就讲到这里惹!看到本蒟蒻那么幸苦,点个赞再走呗~

posted @ 2022-11-23 19:42  Random_life  阅读(44)  评论(0编辑  收藏  举报