没有上司的舞会(树形dp入门题)

洛谷 P1352没有上司的舞会

题目描述

*某大学有 n 个职员,编号为 1…n。
他们之间有从属关系,也就是说他们的关系就像一棵以校长为根的树,父结点就是子结点的直接上司。现在有个周年庆宴会,宴会每邀请来一个职员都会增加一定的快乐指数 r [ i ] r[ i ] r[i],但是呢,如果某个职员的直接上司来参加舞会了,那么这个职员就无论如何也不肯来参加舞会了。所以,请你编程计算,邀请哪些职员可以使快乐指数最大,求最大的快乐指数。

输入格式

输入的第一行是一个整数 n n n
第 2 到第 ( n + 1 ) (n + 1) (n+1) 行,每行一个整数,第 ( i + 1 ) (i + 1) (i+1) 行的整数表示 ii 号职员的快乐指数 r [ i ] r[ i ] r[i]
( n + 2 ) (n + 2) (n+2)到第 ( 2 n + 1 ) (2n+1) (2n+1) 行,每行输入一对整数 l , k l, k l,k,代表 k k k l l l 的直接上司。

输出格式

输出一行一个整数代表最大的快乐指数。

样例输入
7
1
1
1
1
1
1
1
1 3
2 3
6 4
7 4
4 5
3 5
样例输出
5
说明/提示

数据规模与约定
对于 100% 的数据,保证
1 ≤ n ≤ 6 ∗ 10 ³ 1 ≤ n ≤ 6*10³ 1n610³
− 128 ≤ r [ i ] ≤ 127 -128 ≤ r[ i ] ≤ 127 128r[i]127
1 ≤ l , k ≤ n 1 ≤ l,k ≤ n 1l,kn
且给出的关系一定是一棵树。

一道入门级别的树形dp。
在攻克题目之前,我们先来分析一下树形dp的运作方式。
树形dp为了保证取点最优,必须从叶节点不断推向根节点(废话 ),求得最右值。众所周知,根节点是没有爸爸(上司)的,我们可以亲切地称呼他为孤儿(手动滑稽^ _ ^)

然后开始分析样例:(注:图1为编号关系,图2为快乐值)

直属上司
直属上司
直属上司
直属上司
直属上司
直属上司
直属上司
直属上司
直属上司
直属上司
直属上司
直属上司
1
2
3
4
5(无直属上司)
6
7
1(无直属上司)
1
1
1
1
1
1

是不是发现只有5没有直属上司(孤儿 )?这个(孤儿 ) 就是可爱的根节点,那我们该怎么寻找呢?
别急啊,我们先建树。
建树的方法主要有两种:
1.STL v e c t o r vector vector 建树
2.链式前向星建树
由于本博主更喜欢STL中的vector建树(说白了就是不咋会链式前向星
所以,关于vector的学习,你们可以去一位大犇的博客看看。
网址点我
首先,我们通过 v e c t o r vector vector来定义 s o n [ 6005 ] son[6005] son[6005],即 v e c t o r < i n t > s o n vector<int>son vector<int>son
s o n [ i ] son[i] son[i]来保存 i i i结点的所有儿子,建树。
定义root,运用root寻找根节点:

for (register int i = 1; i < n; i++) {
    	int k, l;
    	scanf ("%d%d", &k, &l);
    	son[l].push_back(k);//记录
    	v[k] = 1; //因为l是k的直属上司,所以k一定不是孤儿
    }
    int root = 0;
    for (register int i = 1; i <= n; i++){//一起找孤儿 
    	if (!v[i]){//如果没有被记录过,那么证明他是孤儿
    		root = i; //找孤儿的位置(根节点位置)
    		break;
    	}
    }

定义二维数组 f [ 6005 ] [ 2 ] f[6005][2] f[6005][2],
f [ i ] [ 0 ] f[i][0] f[i][0]定义上司去,下属不去
f [ i ] [ 1 ] f[i][1] f[i][1]定义上司不去,下属去
则得出动态转移方程:(y是x的儿子)
上司去,下属不去: f [ x ] [ 0 ] + = m a x ( f [ y ] [ 0 ] , f [ y ] [ 1 ] ) f[x][0] += max(f[y][0],f[y][1]) f[x][0]+=max(f[y][0],f[y][1])
上司不去,下属去: f [ x ] [ 1 ] + = f [ y ] [ 0 ] f[x][1] += f[y][0] f[x][1]+=f[y][0]

f[x][0] += max (f[y][1], f[y][0]);//上司不去,下属去或不去 
f[x][1] += f[y][0]; // 上司去,下属不去

分析结束上代码:
A C C o d e : AC Code: ACCode:

#include <bits/stdc++.h>
#define Max 6005 
using namespace std;
int n, r[Max], v[Max], f[Max][2];
vector< int >son[Max];//STL
inline void dp (int x) {//dp
	f[x][0] = 0;
	f[x][1] = r[x];
    for (register int i = 0; i < son[x].size(); i++) {
		int y = son[x][i];
		dp (y);//递归 
		f[x][0] += max (f[y][1], f[y][0]);//上司不去,下属去或不去 
		f[x][1] += f[y][0]; // 上司去,下属不去
	} 
	return;
}
int main() {
	memset(v, 0, sizeof(v));//记得清零(预处理)
    scanf ("%d", &n);
    for (register int i = 1; i <= n; i++)
        scanf("%d", &r[i]);
    for (register int i = 1; i < n; i++) {
    	int k, l;
    	scanf ("%d%d", &k, &l);
    	son[l].push_back(k);
    	v[k] = 1; //因为l是k的直属上司,所以k一定不是孤儿
    }
    int root = 0;
    for (register int i = 1; i <= n; i++) {//一起找孤儿 
    	if (!v[i]) {
    		root = i; //找孤儿 
    		break;
    	}
    }
    dp (root);//dp 
    return !printf ("%d", max (f[root][1], f[root][0]));//省了一行
}

拜拜

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值