设 S={x1,x2,...,xn} 是有序集,且 x1<x2<...<xn,表示有序集S的二叉搜索树利用二叉树的结点来存储有序集中的元素。在该二叉搜索树中搜索一个元素x,结果有两种情形:
-
在二叉搜索树的内结点中找到 x=xi(即找到了实结点),设这种情况的概率为 pi
-
在二叉搜索树的叶节点中确定 x∈(xi,xi+1)(即找到了虚结点),设这种情况的概率为 qi
设根节点的深度为0,存储元素 xi的结点深度为 ci,叶节点(xj,xj+1)的结点深度为 dj,在该二叉搜索树中进行一次搜索所需的平均比较次数为 m,那么有公式:
m=∑i=1npi(1+ci)+∑j=0nqjdj
m又称为二叉搜索树 T 的平均路长,本题的要求是对于有序集 S 及其存取概率分布(q0,p1,q1,..,pn,qn),在所有表示有序集 S 的二叉搜索树中找出一颗具有最小平均路长的二叉搜索树。
输入格式:
第一行给出有序集中的元素数量 n (2<n<10)
第二行给出内结点(实结点)的存取概率分布 p1,..,pn
第三行给出叶节点(虚结点)的存取概率分布 q0,q1,..,qn
输出格式:
第一行输出最小 m ,保留两位小数。
第二行先序遍历输出二叉树,对于实结点输出其编号,对于虚结点输出.
,每一个符号后有一个空格,若有多个满足要求的二叉树,则输出根节点编号最小的。
输入样例:
3
0.50 0.1 0.05
0.15 0.1 0.05 0.05
输出样例:
1.50
1 . 2 . 3 . .
参考代码:
#include<bits/stdc++.h> // 引入C++标准库
using namespace std; // 使用标准命名空间
const int M = 100; // 定义常量M为100
double C[M][M], W[M][M], p[M], q[M]; // 定义二维数组C、W、p和q,用于存储最优二叉搜索树的代价和权重
int S[M][M]; // 定义二维数组S,用于存储最优二叉搜索树的分割点
int n, i, j, k; // 定义整型变量n、i、j、k
// 函数Optimal_BST:计算最优二叉搜索树的代价和权重
void Optimal_BST()
{
for (i=1;i<=n;i++) // 初始化第一行的代价和权重
{
C[i][i - 1] = 0.0;
W[i][i - 1] = q[i - 1];
}
for (int t=1;t<=n;t++) // 遍历所有可能的分割点
{
for (i = 1; i <= n - t + 1; i++) // 遍历所有可能的左子树长度
{
j = i + t - 1; // 计算右子树的长度
W[i][j] = W[i][j - 1] + p[j] + q[j]; // 计算当前分割点的权重
C[i][j] = C[i][i - 1] + C[i + 1][j]; // 计算当前分割点的代价
S[i][j] = i; // 初始化当前分割点的分割点为i
for (k = i + 1; k < j; k++) // 遍历所有可能的分割点
{
double tmp = C[i][k - 1] + C[k + 1][j]; // 计算将当前分割点作为分割点的最优代价
if (tmp<C[i][j] && fabs(tmp - C[i][j])>1E-6) // 如果新的最优代价小于当前最优代价,则更新最优代价和分割点
{
C[i][j] = tmp;
S[i][j] = k;
}
}
C[i][j] += W[i][j]; // 加上当前分割点的权重
}
}
}
// 函数Construct_Optimal_BST:构造最优二叉搜索树
void Construct_Optimal_BST(int i,int j,bool flag)
{
if (flag == 0) // 如果flag为0,表示这是第一次调用该函数,需要输出分割点
{
printf("%d ",S[i][j]); // 输出分割点
flag = 1; // 将flag设为1,表示已经输出过分割点
}
int k = S[i][j]; // 获取当前分割点的分割点
if (k - 1 < i) // 如果分割点在左子树中,需要输出"."
{
printf(". ");
}
else // 如果分割点不在左子树中,需要输出分割点的左子树的分割点
{
printf("%d ",S[i][k-1]);
Construct_Optimal_BST(i, k - 1, 1); // 递归调用该函数,构造左子树的最优二叉搜索树
}
if (k>=j) // 如果分割点在右子树中,需要输出"."
{
printf(". ");
}
else // 如果分割点不在右子树中,需要输出分割点的右子树的分割点
{
printf("%d ",S[k+1][j]);
Construct_Optimal_BST(k + 1, j, 1); // 递归调用该函数,构造右子树的最优二叉搜索树
}
}
int main()
{
cin >> n; // 输入节点数n
for (i = 1; i <= n; i++) // 输入每个节点的权重p[i]
{
cin >> p[i];
}
for (i = 0; i <= n; i++) // 输入每个节点的代价q[i]
{
cin >> q[i];
}
Optimal_BST(); // 计算最优二叉搜索树的代价和权重
printf("%.2lf\n",C[1][n]); // 输出最优二叉搜索树的代价
Construct_Optimal_BST(1, n, 0); // 构造最优二叉搜索树并输出
system("pause"); // 暂停程序,等待用户按任意键继续
return 0; // 返回0,表示程序正常结束
}