没有上司的晚会 弱弱的题解 (话说我终于会树形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])\)
撒花
好了,这道题就讲到这里惹!看到本蒟蒻那么幸苦,点个赞再走呗~