定义
最优二叉搜索树(optimal binary search tree) :给定一个由 n n n 个互不相同的关键字组成的序列 K = ⟨ k 1 , k 2 , … , k n ⟩ K=⟨k1,k2,…,kn⟩ K = ⟨ k 1 , k 2 , … , kn ⟩ ,其中 k 1 < k 2 < ⋯ < k n k1<k2<⋯<kn k 1 < k 2 < ⋯ < kn ,用这些关键字构造一棵二叉搜索树。对于每个关键字 k i ki ki ,查找其的的概率为p i pi p i ,可能有些被查找的关键字不在 K K K 中,我们需要构造 n + 1 n+1 n + 1 个虚关键字 d 0 , d 1 , d 2 , … , d n d0,d1,d2,…,dn d 0 , d 1 , d 2 , … , d n ,其中 d 0 < k 1 d0<k1 d 0 < k 1 , d n > k n dn>kn d n > kn , k i < d i < k i + 1 ki<di<ki+1 ki < d i < ki + 1 ,其中 i = 1 , 2 , … , n − 1 i=1,2,…,n−1 i = 1 , 2 , … , n − 1 ,对于每个关键字 d i di d i ,查找其的的概率为 q i qi q i 。每个关键字 k i ki ki 对应二叉搜索树中一个内部结点,每个虚关键字 d i di d i 对应二叉搜索树中一个叶结点,这样每次查找关键字如果查找成功,那么最终落在 k i ki ki ,如果查找失败,那么最终落在 d i di d i 。且有∑ i = 1 n p i + ∑ i = 0 n q i = 1 \sum_{i=1}^np_i+\sum_{i=0}^nq_i=1 ∑ i = 1 n p i + ∑ i = 0 n q i = 1 。
题意
设二叉搜索树的关键字按从小到大的顺序为 a1,a2,…,an(∀i<j,ai<aj),搜索频率表示为 q0,p1,q1,p2,q2,…,qn−1,pn,qn,其中 pi 表示搜索关键字是 ai 的频率,qi 表示搜索关键字在 ai−1 和 ai 之间的频率(这里我们令 a0=−∞,an+1=+∞)。定义搜索代价为这次搜索访问的结点个数,对于存在的关键字,代价为对应结点的深度;对于不存在的结点,代价为某个虚结点的深度。求代价的最小期望。
题解
代价计算公式
一棵树的期望搜素代价为根节点到每个节点的查找次数(深度+1)乘以该节点的搜索概率的乘积之和。
E [ search cost in T ] = ∑ i = 1 n ( d e p t h T ( k i ) + 1 ) ⋅ p i + ∑ i = 0 n ( d e p t h T ( d i ) + 1 ) ⋅ q i = ∑ i = 1 n d e p t h T ( k i ) p i + ∑ i = 0 n d e p t h T ( d i ) q i + ∑ i = 1 n p i + ∑ i = 0 n q i = 1 + ∑ i = 1 n d e p t h T ( k T ) ⋅ p i + ∑ i = 0 n d e p t h T ( d i ) ⋅ q i \begin{aligned}\operatorname{E}[\text{search cost in }T]
&=\sum_{i=1}^n(\mathrm{depth}_T(k_i)+1)\cdot p_i+\sum_{i=0}^n(\mathrm{depth}_T(d_i)+1)\cdot q_i\\
&=\sum_{i=1}^{n} depth_T(k_i)p_i +\sum_{i=0}^{n}depth_T(d_i)q_i + \sum_{i=1}^{n} p_i +\sum_{i=0}^{n} q_i\\
&=1+\sum_{i=1}^n\mathrm{depth}_T(k_T)\cdot p_i+\sum_{i=0}^n\mathrm{depth}_T(d_i)\cdot q_i\\
\end{aligned} E [ search cost in T ] = i = 1 ∑ n ( depth T ( k i ) + 1 ) ⋅ p i + i = 0 ∑ n ( depth T ( d i ) + 1 ) ⋅ q i = i = 1 ∑ n d e pt h T ( k i ) p i + i = 0 ∑ n d e pt h T ( d i ) q i + i = 1 ∑ n p i + i = 0 ∑ n q i = 1 + i = 1 ∑ n depth T ( k T ) ⋅ p i + i = 0 ∑ n depth T ( d i ) ⋅ q i
确定状态
E [ i , j ] E[i,j] E [ i , j ] 为关键字k i , k i + 1 , … , k j k_i,k_{i+1},\dots ,k_{j} k i , k i + 1 , … , k j 组成的二叉搜索树的搜索期望代价。
E [ i , j ] = ∑ i = 1 n d e p t h T ( k i ) p i + ∑ i = 0 n d e p t h T ( d i ) q i + ∑ i = 1 n p i + ∑ i = 0 n q i E[i,j]=\sum_{i=1}^{n} depth_T(k_i)p_i +\sum_{i=0}^{n}depth_T(d_i)q_i + \sum_{i=1}^{n} p_i +\sum_{i=0}^{n} q_i
E [ i , j ] = i = 1 ∑ n d e pt h T ( k i ) p i + i = 0 ∑ n d e pt h T ( d i ) q i + i = 1 ∑ n p i + i = 0 ∑ n q i
w [ i , j ] w[i,j] w [ i , j ] 为关键字k i , k i + 1 , … , k j k_i,k_{i+1},\dots ,k_{j} k i , k i + 1 , … , k j 组成的二叉搜索树的搜索概率和。
w [ i , j ] = ∑ l = i j p l + ∑ l = i − 1 j q i w[i,j]=\sum_{l=i}^jp_l+\sum_{l=i-1}^jq_i
w [ i , j ] = l = i ∑ j p l + l = i − 1 ∑ j q i
确定状态转移方程
若存在一个r r r 在[ i , j ] [i,j] [ i , j ] 中,且k r k_r k r 为根节点,则其左子树键序列为< k i , k i + 1 , … , k r − 1 > <k_i,k_{i+1},\dots,k_{r-1}> < k i , k i + 1 , … , k r − 1 > ,右子树键序列为< k r + 1 , … , k j > <k_{r+1},\dots,k_j> < k r + 1 , … , k j > ,将上述式子拆分成左右子树两个部分。
注:在子树中搜索一个节点的查找次数为d e p t h + 1 depth+1 d e pt h + 1 ,而子树在整棵树中的查找次数中还要加上查找整棵树的根节点一次,故为d e p t h + 2 depth+2 d e pt h + 2 。
E [ i , j ] = p r + ∑ l = i r − 1 ( d e p t h T l e f t ( k l ) + 2 ) p i + ∑ l = i − 1 r − 1 ( d e p t h T l e f t ( d l ) + 2 ) q i + ∑ l = r + 1 j ( d e p t h T r i g h t ( k l ) + 2 ) p i + ∑ l = r j ( d e p t h T r i g h t ( d l ) + 2 ) q i = p r + E [ i , r − 1 ] + E [ r + 1 , j ] + ∑ l = i r − 1 p l + ∑ l = i − 1 r − 1 q l + ∑ l = r + 1 j p l + ∑ l = r j q l = p r + E [ i , r − 1 ] + E [ r + 1 , j ] + w [ i , r − 1 ] + w [ r + 1 , j ] = E [ i , r − 1 ] + E [ r + 1 , j ] + w [ i , j ] \begin{aligned}
E[i,j]&= p_r+ \sum_{l=i}^{r-1}(depth_{T_{left}}(k_l)+2)p_i+\sum_{l=i-1}^{r-1}(depth_{T_{left}}(d_l)+2)q_i \\ &+\sum_{l=r+1}^{j}(depth_{T_{right}}(k_l)+2)p_i+\sum_{l=r}^{j}(depth_{T_{right}}(d_l)+2)q_i\\
&=p_r+E[i,r-1]+E[r+1,j]+\sum_{l=i}^{r-1} p_l +\sum_{l=i-1}^{r-1} q_l+\sum_{l=r+1}^{j} p_l +\sum_{l=r}^{j} q_l\\
&=p_r+E[i,r-1]+E[r+1,j]+w[i,r-1]+w[r+1,j]\\
&=E[i,r-1]+E[r+1,j]+w[i,j]
\end{aligned}
E [ i , j ] = p r + l = i ∑ r − 1 ( d e pt h T l e f t ( k l ) + 2 ) p i + l = i − 1 ∑ r − 1 ( d e pt h T l e f t ( d l ) + 2 ) q i + l = r + 1 ∑ j ( d e pt h T r i g h t ( k l ) + 2 ) p i + l = r ∑ j ( d e pt h T r i g h t ( d l ) + 2 ) q i = p r + E [ i , r − 1 ] + E [ r + 1 , j ] + l = i ∑ r − 1 p l + l = i − 1 ∑ r − 1 q l + l = r + 1 ∑ j p l + l = r ∑ j q l = p r + E [ i , r − 1 ] + E [ r + 1 , j ] + w [ i , r − 1 ] + w [ r + 1 , j ] = E [ i , r − 1 ] + E [ r + 1 , j ] + w [ i , j ]
且当j = i − 1 j = i - 1 j = i − 1 时,此时子树中只有虚拟键,期望搜索代价为E [ i , i − 1 ] = q i − 1 E[i,i - 1] = q_{i-1} E [ i , i − 1 ] = q i − 1 .
故状态确定转移方程为
E [ i , j ] = { q i − 1 when j = i − 1 , min i ≤ r ≤ j { E [ i , r − 1 ] + E [ r + 1 , j ] + w ( i , j ) } when i ≤ j . E[i,j]=\begin{cases}q_{i-1}&\text{when }j=i-1\:,\\\min_{i\leq r\leq j}\{E[i,r-1]+E[r+1,j]+w(i,j)\}&\text{when }i\leq j\:.\end{cases}
E [ i , j ] = { q i − 1 min i ≤ r ≤ j { E [ i , r − 1 ] + E [ r + 1 , j ] + w ( i , j )} when j = i − 1 , when i ≤ j .
代码实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 #include <bits/stdc++.h> using namespace std;const int N = 1e3 +5 ;const double INF = 1.0 /0.0 ;int n;double p[N],q[N];double dp[N][N],w[N][N];signed main () { cin>>n; for (int i=1 ;i<=n;i++) { cin>>p[i]; } for (int i=0 ;i<=n;i++) { cin>>q[i]; } for (int i=1 ;i<=n+1 ;i++) { dp[i][i-1 ] = q[i-1 ]; w[i][i-1 ] = q[i-1 ]; } for (int k=1 ;k<=n;k++) { for (int i = 1 ; i<=n-k+1 ;i++) { int j = i+k-1 ; dp[i][j] = INF; w[i][j] = w[i][j-1 ]+p[j]+q[j]; for (int r=i;r<=j;r++) { double val = dp[i][r-1 ]+dp[r+1 ][j]+w[i][j]; if (val<dp[i][j]) dp[i][j]=val; } } } cout<<dp[1 ][n]<<"\n" ; return 0 ; }