P1352 SSL1607没有上司的晚会题解(链式前向星)

3 篇文章 0 订阅

【题目链接】

P1352

【前置知识】

这题是一道树形 d p dp dp 的模板题,我们要用到一种新的存储结构——链式前向星
首先我们先来了解一下链式前向星的原理

利用链式储存结构。对于每一个顶点,开一条链,依次存储以该点为起点的边。
										————PPT

那我们应该如何实现呢?
首先,我们需要一个结构体数组 e e e,用这个数组来存储边的信息,其中我们需要存储三个信息: e x e_x ex 当前点, e y e_y ey 所连接的点, e n e x t e_{next} enext 所连接的点的下一个点

struct NOTE
{
	int x,y,next;
}e[N];

下面是如何建边,我们还需要有一个数组 h e a d head head
h e a d i head_i headi 存储 i i i 这个顶点对应的链的起始位置。

void add(int x,int y)//两个点之间建边
{
	e[++tot].x=x;//存当前点
	e[tot].y=y;//它所连接的点
	e[tot].next=head[x];//x上一个点的位置
	head[x]=tot;//它上一个点的位置在哪里
}

借助题目数据来理解一下

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

在这里插入图片描述

给出一张表方便理解这个过程
在这里插入图片描述

【过程】

第一次是1 3建边
e x = 3 e_x=3 ex=3
e y = 1 e_y=1 ey=1
因为它是这条链的起点所以
e n e x t = 0 e_{next}=0 enext=0
h e a d 3 = 1 head_3=1 head3=1

第二次是2 3建边
e x = 3 e_x=3 ex=3
e y = 2 e_y=2 ey=2
e x = 3 e_x=3 ex=3 链接这 1 1 1 这个位置
e n e x t = 1 e_{next}=1 enext=1
h e a d 3 = 2 head_3=2 head3=2 链接的位置移到了这里

第三次是6 4建边
e x = 4 e_x=4 ex=4
e y = 6 e_y=6 ey=6
因为它是这条链的起点所以
e n e x t = 0 e_{next}=0 enext=0
h e a d 4 = 3 head_4=3 head4=3

第四次是7 4建边
e x = 4 e_x=4 ex=4
e y = 7 e_y=7 ey=7
e x = 4 e_x=4 ex=4 链接这 3 3 3 这个位置
e n e x t = 3 e_{next}=3 enext=3
h e a d 4 = 4 head_4=4 head4=4 链接的位置移到了这里

第五次是4 5建边
e x = 5 e_x=5 ex=5
e y = 4 e_y=4 ey=4
因为它是这条链的起点所以
e n e x t = 0 e_{next}=0 enext=0
h e a d 5 = 5 head_5=5 head5=5

第六次是3 5建边
e x = 5 e_x=5 ex=5
e y = 3 e_y=3 ey=3
e x = 5 e_x=5 ex=5 链接这 5 5 5 这个位置
e n e x t = 5 e_{next}=5 enext=5
h e a d 5 = 6 head_5=6 head5=6 链接的位置移到了这里

【注意事项】

最后注意一下:这里是因为题目说了

每行输入一对整数 l, kl,k,代表 kk 是 ll 的直接上司

所以只需要建一次边,但是当这个图是无向时,我们还需要正反两次边。

【解题思路】

言归正传,我们来讲一下这道题到底怎么做?
这道题我们需要用到一些分类讨论的思想。
定义 d i , 0 d_{i,0} di,0 为不选第 i i i 个节点的最大的快乐指数
d i , 1 d_{i,1} di,1 为选第 i i i 个节点的最大的快乐指数
如果选这个节点,那么它的子节点一定不能选,则 d i , 1 = d j , 0 d_{i,1}=d_{j,0} di,1=dj,0
如果不选这个节点,那么它的子节点可选可不选,则 d i , 1 = m a x ( d j , 0 , d j , 1 ) d_{i,1}=max(d_{j,0},d_{j,1}) di,1=max(dj,0,dj,1)
动态转移方程
d i , k = { d j , 0 k = 0 m a x ( d j , 1 , d j , 0 ) k = 1 d_{i,k}=\begin{cases} d_{j,0}&k=0\\ max(d_{j,1},d_{j,0})&k=1 \end{cases} di,k={dj,0max(dj,1,dj,0)k=0k=1

【CODE】

#include<iostream>
#include<cstdio>
using namespace std;
int head[6010],d[6001][2];
int h[6010],v[6010];
int n,tot,xx,yy,maxn=-0x7ffffff,gen;
struct note
{
	int x,y,nxt;
}e[6010];
void add(int x,int y)
{
	e[++tot].x=x;
	e[tot].y=y;
	e[tot].nxt=head[x];
	head[x]=tot;
}
void dp(int nw)
{
	d[nw][1]=h[nw];//初始化选自己
	for (int i=head[nw];i;i=e[i].nxt)//序号,往链表的前面找
	{
		dp(e[i].y);//子节点
		d[nw][1]=d[e[i].y][0]+d[nw][1];//dp
		d[nw][0]=max(d[e[i].y][1],d[e[i].y][0])+d[nw][0];
	}
}
int main()
{
	cin>>n;
	for (int i=1;i<=n;i++)
		cin>>h[i];
	for (int i=1;i<n;i++)
	{
		cin>>xx>>yy;
		add(yy,xx);//两个点之间建一条边
		v[xx]=1;//这个点为子节点
	}
	//找根节点
	for (int i=1;i<=n;i++)
		if (v[i]==0)
		{
			gen=i;
			break;
		}
	dp(gen);//从根节点开始搜索
	cout<<max(d[gen][0],d[gen][1]);//选较大的输出
	return 0;
 } 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值