「SCOI2006」zh_tree

「SCOI2006」zh_tree

题目限制

  • 内存限制:250.00MB
  • 时间限制:1.00s
  • 标准输入输出

题目知识点

  • 思维
  • 动态规划 d p dp dp
    • 区间 d p dp dp

题目来源

「 洛谷 」P4539 [SCOI2006]zh_tree

题目

题目背景

张老师根据自己工作的需要,设计了一种特殊的二叉搜索树

题目描述

他把这种二叉树起名为 zh_tree,对于具有 n n n 个节点的 zh_tree,其中序遍历恰好为 ( 1 、 2 、 3 、 ⋅ ⋅ ⋅ 、 n ) (1、2、3、···、n) (123⋅⋅⋅n),其中数字 1 、 2 、 3 、 ⋅ ⋅ ⋅ 、 n 1、2、3、···、n 123⋅⋅⋅n 是每个节点的编号
n n n 个结点恰好对应于一组学术论文中出现的 n n n 个不同的单词

i i i 个单词在该组论文中出现的次数记为 d i d_i di,例如, d 2 = 10 d_2 = 10 d2=10 表示第 2 2 2 个节点所对应的单词在该组论文中出现了 10 10 10
设该组论文中出现的单词总数为 S S S,即 S = d 1 + d 2 + ⋅ ⋅ ⋅ + d n = ∑ i = 1 n d i S = d_1 + d_2 + ··· + d_n = \sum _{i = 1} ^{n} d_i S=d1+d2+⋅⋅⋅+dn=i=1ndi
f i = d i S f_i = \frac{d_i}{S} fi=Sdi 为第 i i i 个单词在该组论文中出现的 概率(频率)

张老师把根节点深度规定为 0 0 0,如果第 i i i 个节点的深度为 r i r_i ri,则访问该节点的代价为 h i h_i hi h i = k ( r i + 1 ) + c h_i = k(r_i + 1) + c hi=k(ri+1)+c,其中 k 、 c k、c kc 为已知的常数 ( 0 < k , c ≤ 100 ) (0 < k, c \leq 100) (0<k,c100)

zh_tree 是满足 h 1 f 1 + h 2 f 2 + ⋅ ⋅ ⋅ + h n f n = ∑ i = 1 n h i f i h_1f_1 + h_2f_2 + ··· + h_nf_n = \sum _{i = 1} ^ {n} h_if_i h1f1+h2f2+⋅⋅⋅+hnfn=i=1nhifi 最小的一棵二叉树
我们称上式为访问 zh_tree 的代价
请你根据已知的数据为张老师设计一棵 zh_tree

格式

输入格式

输入共 2 2 2 行:
对于第 1 1 1 行, 3 3 3 个用空格隔开的正数, n 、 k 、 c n、k、c nkc。其中 n < 30 n < 30 n<30,为整数; k 、 c k、c kc 为不超过 100 100 100 的正实数
对于第 2 2 2 行: n n n 个用空格隔开的正整数,为每个单词出现的次数 d i ( d i < 200 ) d_i(d_i < 200) di(di<200)

输出格式

输出一行:一个正实数,保留 3 3 3 位小数,表示访问 zh_tree 的最小代价

样例

样例输入

4 2 3.5
20 30 50 20

样例输出

7.000

提示

数据范围

对于 100 % 100\% 100% 的数据: 1 ≤ n < 30 1 \leq n < 30 1n<30 0 < k 、 c ≤ 100 0 < k、c \leq 100 0<kc100 d i < 200 d_i < 200 di<200


思路

对于每个节点的深度 r i r_i ri,由于 h i = k ( r i + 1 ) + c h_i = k(r_i + 1) + c hi=k(ri+1)+c,不妨可以把根节点的深度改为 1 1 1,这样就可以直接用 现在的 r i r_i ri 表示 原来的 r i + 1 r_i + 1 ri+1 了。

先把 ∑ i = 1 n h i f i \sum\limits_{i = 1}^n h_i f_i i=1nhifi 化简:
∑ i = 1 n ( h i × f i ) = ∑ i = 1 n ( k × r i + c ) × d i S = 1 S × ∑ i = 1 n ( k × r i + c ) × d i = 1 S × ∑ i = 1 n ( k × r i × d i + c × d i ) = 1 S × ( ∑ i = 1 n k × r i × d i + ∑ i = 1 n c × d i ) = ( 1 S × ∑ i = 1 n k × r i × d i ) + ( 1 S × c × ∑ i = 1 n d i ) = ( k S × ∑ i = 1 n r i × d i ) + ( 1 S × c × S ) = k × ∑ i = 1 n r i × d i S + c \begin{aligned} \sum\limits_{i = 1}^n (h_i \times f_i) & = \sum\limits_{i = 1}^n (k \times r_i + c) \times \dfrac {d_i} S \\ & = \dfrac 1 S \times \sum\limits_{i = 1} ^{n} (k \times r_i + c) \times d_i \\ & = \dfrac 1 S \times \sum\limits_{i = 1}^n (k \times r_i \times d_i + c \times d_i) \\ & = \dfrac 1 S \times (\sum\limits_{i = 1}^n k \times r_i \times d_i + \sum\limits_{i = 1}^n c \times d_i) \\ & = (\dfrac 1 S \times \sum\limits_{i = 1}^n k \times r_i \times d_i) + (\dfrac 1 S \times c \times \sum\limits_{i = 1} ^{n} d_i) \\ & = (\dfrac k S \times \sum\limits_{i = 1}^n r_i \times d_i) + (\dfrac 1 S \times c \times S) \\ & = \dfrac {k \times \sum\limits_{i = 1}^n r_i \times d_i}{S} + c \\ \end{aligned} i=1n(hi×fi)=i=1n(k×ri+c)×Sdi=S1×i=1n(k×ri+c)×di=S1×i=1n(k×ri×di+c×di)=S1×(i=1nk×ri×di+i=1nc×di)=(S1×i=1nk×ri×di)+(S1×c×i=1ndi)=(Sk×i=1nri×di)+(S1×c×S)=Sk×i=1nri×di+c

题目要我们求 ∑ i = 1 n h i f i \sum\limits_{i = 1}^n h_i f_i i=1nhifi 的最小值,就可以转化成求 ∑ i = 1 n r i × d i \sum\limits_{i = 1}^n r_i \times d_i i=1nri×di 的最小值。

  • 状态定义:设 f L , R f_{L, R} fL,R 表示:一棵二叉树的 中序遍历(左子树,根节点,右子树) L ∼ R ( L , L + 1 , L + 2 , ⋯   , R ) L \sim R(L, L + 1, L + 2, \cdots, R) LR(L,L+1,L+2,,R) ∑ i = L R r i × d i \sum\limits_{i = L}^R r_i \times d_i i=LRri×di 的最小值,最终的答案就是 k × f 1 , n S + c \dfrac{k \times f_{1, n}}{S} + c Sk×f1,n+c
  • 初始化: ∀ L = R = i , f L , R = d i \forall L = R = i, f_{L, R} = d_i L=R=i,fL,R=di ∀ L < R , f L , R = + ∞ \forall L < R, f_{L, R} = + \infty L<R,fL,R=+
  • 状态转移:对于中序遍历为 L ∼ R L \sim R LR 的二叉树,枚举其根节点 t r ( L ≤ t r ≤ R ) tr(L \leq tr \leq R) tr(LtrR) f L , R = min ⁡ t r = L R { f L , t r − 1 + f t r + 1 , R + ∑ i = L R d i } = min ⁡ t r = L R { f L , t r − 1 + f t r + 1 , R } + ∑ i = L R d i f_{L, R} = \min\limits_{tr = L}^R \{ f_{L, tr - 1} + f_{tr + 1, R} + \sum\limits_{i = L}^{R} d_i \} = \min\limits_{tr = L}^R \{ f_{L, tr - 1} + f_{tr + 1, R} \} + \sum\limits_{i = L}^{R} d_i fL,R=tr=LminR{fL,tr1+ftr+1,R+i=LRdi}=tr=LminR{fL,tr1+ftr+1,R}+i=LRdi

如果需要输出方案,可以在转移过程中记录下 f L , R f_{L, R} fL,R 取最小值时的根节点,最后递归即可。

转移时 ∑ i = L R d i \sum\limits_{i = L}^{R} d_i i=LRdi 可以用前缀和作差,这样时间复杂度 O ( n 4 ) → O ( n 3 ) O(n ^ 4) \to O(n ^ 3) O(n4)O(n3),不过都可以 AC \texttt{AC} AC


代码

#include <cstdio>
#include <cstring>

#define Min(a, b) ((a) < (b) ? (a) : (b))

const int MAXN = 30;

int N, S;
int D[MAXN + 5];
int Pre[MAXN + 5]; // 前缀和优化 

double K, C;
double dp[MAXN + 5][MAXN + 5];

int main()
{
	memset(dp, 0x3f, sizeof(dp));
	scanf("%d %lf %lf", &N, &K, &C);
	for (int i = 1; i <= N; i++)
	{
		scanf("%d", &D[i]);
		Pre[i] = S += D[i];
		dp[i][i] = 1.0 * D[i];
	}
	for (int len = 2; len <= N; len++)
	{
		for (int i = 1, j = len; j <= N; i++, j++)
		{
			dp[i][j] = Min(dp[i][j - 1], dp[i + 1][j]);
			for (int k = i + 1; k <= j - 1; k++)
				dp[i][j] = Min(dp[i][j], dp[i][k - 1] + dp[k + 1][j]);
			dp[i][j] += 1.0 * (Pre[j] - Pre[i - 1]);
		}
	}
	printf("%.3lf\n", K * dp[1][N] / S + C);
	return 0;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值