算法设计与分析——动态规划

news2024/10/26 1:21:23

1.动态规划基础

1.1动态规划的基本思想

        动态规划建立在最优原则的基础上,在每一步决策上列出可能的局部解,按某些条件舍弃不能得到最优解的局部解,通过逐层筛选减少计算量。每一步都经过筛选,以每一步的最优性来保证全局的最优性。具体来说,动态规划算法仍然是将待求解的问题的若干子问题,采用列表技术,将从小到大的子问题的计算答案存储于一张表中,由于将原问题分解后的各个子问题可能存在重复,所以当重复遇到该子问题时,只需要查表继续问题的求解,而不需要重复计算。所以动态规划算法的基本思想是记录子问题并不断填表。

1.2动态规划的基本要素

        通常一个可以用动态规划算法求解的问题应该具有3个要素:最优子结构、无后效性和子问题重叠性。

        最优子结构:动态规划算法的关键在于正确的找出基本的递推关系式和恰当的边界条件。要做到这一点,必须将原问题分解为几个相互联系的阶段,在每一个子问题的求解中,均利用它前面子问题的最优化结果,依次进行,最有一个子问题所得的最优解就是整个问题的最优解。

        无后效性:将各个阶段依次排好之后,一旦某阶段的状态已经确定,它以前各阶段的状态无法直接影响未来的决策,并且当前状态的决策只是对以往决策的总结。

        子问题重叠性:动态规划计算最优值时,每次计算所产生的子问题并不总是新问题,有些问题被重复计算多次,但是动态规划将这些子问题的解存放在表格中,不需要重复计算,提高了程序的效率。

1.3动态规划的基本方法

        动态规划问题千奇百怪,有诸多变种,但是动态规划具有比较鲜明的特征,即最优子结构和重叠子问题。解决动态规划问题的思路很重要,掌握下面五步之后再加以练习能够解决许多动态规划问题。

  1. 确定dp的含义:dp数组中存放的是每个子问题的最优解。
  2. 推导动态转移方程:在动态规划问题中
  3. dp的初始化
  4. 遍历顺序
  5. 打印表格

2.矩阵连乘问题

        给定n个矩阵\left \{ {A_{1},A_{2},...,A_{n}} \right \},其中矩阵A_{i}的维数为p_{i-1}×p_{i},且A_{i}A_{i+1}是可乘的,考察这n个矩阵的连乘积A_{1}A_{2}...A{n}。由于矩阵乘法满足结合律,所以计算矩阵的连乘可以有许多不同的计算次序,这种计算次序可以用加括号的方式来确定。如何确定计算矩阵连乘积的计算次序,使得依此次序计算矩阵连乘积需要的数乘次数最少?

        设有四个矩阵A、B、C、D,它们的维数分别是:50×10,10×40,40×30,30×5,其完全加括号方式为:(A((BC)D)),(A(B(CD))),((AB)(CD)),(((AB)C)D),((A(BC))D)所需的乘法次数分别为16000,10500,36000,87500,34500。

        对于n个矩阵的连乘积,设其不同的计算次序为p(n)。每种加括号方式都可以分解为两个矩阵的加括号方式:(A_{1}...A_{k})(A_{k+1}...A_{n}),其递推式为:

                                                      p(n)=\sum_{k=1}^{n-1}p(k)p(n-k)

卡特兰数是组合数学中一个常出现在各种计数问题中的数列。其递推式如下:

                                                h(n)=\sum_{k=0}^{n-1}h(k)h(n-k-1)

 该递推关系的解为:C_{n}=\frac{(2n)!}{(n+1)!n!}

 卡特兰数的渐近增长为 C_{n}~\frac{4^{n}}{n^{3/2}\sqrt{\pi }}

2.1分析最优子结构

        设n个矩阵连乘的最佳计算次序为(A_{1}...A_{k})(A_{k+1}...A_{n}),则(A_{1}...A_{k})(A_{k+1}...A_{n})连乘的计算次序都是最优的。矩阵连乘计算次序问题的最优解包含着子问题的最优解。这种性质称为最优子结构性质,问题的最优子结构性质是该问题可用DP方法求解的显著特征。

2.2递归关系建立

        将矩阵连乘积(A_{i}A_{i+1}...A_{j})记为A[i:j],这里i\leqslant jA[i:j]的总计算量为:A[i:k]的计算量加上A[k+1:j]的计算量,再加上A[i:k]A[k+1:j]相乘的计算量。

        设计算A[i:j]的最佳计算次序所对应的乘法次数为m[i:j],则原问题的最优解为m[1:n]

        当i=j时,A[i:j]=A_i,因此m_i=0,i=1,2,...,n

        当i<j时,m[i,j]=min_{i\leqslant k\leqslant j}(m[i,k]+m[k+1,j] + p_{i-1}p_{k}p_{j})

2.3代码分析

#include<stdio.h>
#define N 7
void MatrixChain(int *p,int n,int m[][N],int s[][N])
{
    for(int i = 0; i <= n; ++i) m[i][i] = 0;//自乘的消耗为0
    for(int r = 2; r <= n; ++r)
    {
        for(int i = 1; i <= n - r + 1; ++i)
        {
            int j = i + r - 1;
            m[i][j] = m[i+1][j] + p[i-1]*p[i]*p[j];//试探性的赋值。
            s[i][j] = i;
            for(int k = i + 1; k < j; ++k)
            {
                int t = m[i][k] + m[k+1][j] + p[i-1]*p[k]*p[j];
                if(t < m[i][j])
                {
                    m[i][j] = t;      
                    s[i][j] = k;
                }
            }
        }
    }
}

void Traceback(int i, int j, int s[][N]){
    if(i == j) printf("A%d",i);
    else
    {
        printf("(");
        Traceback(i,s[i][j],s);
        Traceback(s[i][j]+1,j,s);
        printf(")");
     }
}      
 
int main()
{
    int p[N]={30,35,15,5,10,20,24};
    int m[N][N],s[N][N];
    MatrixChain(p,N-1,m,s);
    printf("矩阵的最佳乘积方式为:");
    Traceback(1,6,s);
    return 0;
}

3.电路布线问题

        在一块电路板的上、下两端分别有n个接线柱。根据电路设计,要求用导线(i,\pi (i))将上端接线柱与下端接线柱相连,如图所示:

                

其中\pi(i)\{1,2,...,n\}的一个排列,导线(i,\pi (i))称为该电路的第i条连线。对于任何1\leqslant i< j\leqslant n,第i条连线和第j条连线相交的充分且必要条件是\pi(i)>\pi(j)

        在制作电路板时,要求将这n条连线分布到若干绝缘层上。在同一层上的连线不可相交。电路布线问题要确定将那些连线安排在第一层上,使得该层上要有尽可能多的连线。换句话说,该问题要确定导线集Nets最大不相交子集

3.1最优子结构分析

        记N(i,j)=\{t|(t,\pi (t))\in Nets,t\leqslant i,\pi (t)\leqslant j \}N(i,j)的最大不相交子集为MNS(i,j)Size(i,j)=|MNS(i,j)|

(1)当i=1时,MNS(1,j)=N(1,j)=\{(1,\pi(1))\}

(2)当i>1时,

        若j<\pi(i)。此时,(i,\pi(i)\nsubseteq N(i,j))。所以N(i,j) = N(i-1,j),从而Size(i,j)=Size(i-1,j)

        若j\geq \pi(i)。此时,

        如果(i,\pi(i)\in MNS(i,j)),则对任意的(t,\pi(t)\in \in MNS(i,j))t<i\pi(t)<\pi(i)。在这种情况下MNS(i,j)-\{(i,\pi(i))\}N(i-1,\pi(i)-1)的最大不相交子集,从而Size(i,j)=Size(i-1,\pi(i)-1)+1

        如果(i,\pi(i)\nsubseteq MNS(i,j)),则对任意的(t,\pi(t)\in \in MNS(i,j)),有t<i。从而MNS(i-1,j)\subseteq N(i,j)。因此,Size(i,j)\geqslant Size(i-1,j)。另一方面有MNS(i,j)\subseteq N(i-1,j),因此又有Size(i,j)\leqslant Size(i-1,j),从而有Size(i,j)= Size(i-1,j)

3.2递归关系建立

(1)当i=1时,Size(1,j)=\left\{\begin{matrix} 0~~~~~ j<\pi(1) & \\ 1~~~~~ j \geqslant\pi(1)& \end{matrix}\right.

(2)当i>1时,

                                Size(i,j)=\left\{\begin{matrix} Size(i-1,j) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~j< \pi(i)& \\ max\{Size(i-1,j),Size(i-1,\pi(i)-1)+1\}~~~~~j\geqslant \pi(i)& \end{matrix}\right.

        电路布线问题的最优值为Size(n,n)

3.3代码分析

void MNS(int C[],int n,int **size)
{
    for(int j = 0; j < C[1];++j) size[1][j] = 0;
    for(int j = C[1]; j <= n;++j) size[1][j] = 1;
    for(int i = 2; i < n; ++i)
    {
        for(int j = 1; j < C[i]; ++j)    
            size[i][j] = size[i-1][j];
        for(int j = C[i]; j <= n; ++j)    
            size[i][j] = max(size[i-1][j],size[i-1][C[i]-1]+1);
    }
    size[n][n] = max(size[n-1][n],size[n-1][C[n]-1]+1);
}

4.最长公共子序列 

        若给定子序列X=\{x_{1},x_{2},...,x_{m}\},则Z=\{z_{1},z_{2},...z_{k}\}是X的子序列是指存在一个严格递增下标序列\{i_{1},i_{2},...i_{k}\}使得对于所有的j=1,2,...,kz_{j}=x_{i_{j}}

        给定两个序列XY,当另一序列Z既是X的子序列又是Y的子序列时,称Z是序列XY公共子序列。我们的问题是给定两个序列X=\{x_{1},x_{2},...,x_{m}\}Y=\{y_{1},y_{2},...,y_{n}\},找出XY最长公共子序列

4.1最优子结构分析

        设序列X=\{x_{1},x_{2},...,x_{m}\}Y=\{y_{1},y_{2},...,y_{n}\}的最长公共子序列为Z_{k}=\{z_{1},z_{2},...z_{k}\},则

(1)若x_{m}=y_{n},则z_{k}=x_{m}=y_{n},且Z_{k-1}X_{m-1}Y_{n-1}的最长公共子序列。

(2)若x_{m}\neq y_nz_k\neq x_m,则Z_kX_{m-1}Y_n的最长公共子序列。

(3)若x_{m}\neq y_nz_k\neq y_n,则Z_kX_{m}Y_{n-1}的最长公共子序列。

由此可见,两个序列的最长公共子序列包含了这两个序列的前缀的最长公共子序列。因此,最长公共子序列问题具有最优子结构性质

4.2递归关系建立

       设二维数组c[i][j]记录序列X_iY_j的最长公共子序列的长度。其中X=\{x_{1},x_{2},...,x_{m}\}Y=\{y_{1},y_{2},...,y_{n}\}。当i=0j=0时,空序列是它们的最长公共子序列,此时c[i][j]=0其它情况下:

        c[i][j]=\left\{\begin{matrix} 0~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~i=0~or~j=0\\ c[i-1][j-1]+1~~~~~~~~~~~~~~~~~~i,j>0~and~x_i=y_j\\ max\{c[i][j-1],c[i-1][j]\}~~~~~~i,j>0~and~x_i\neq y_j\end{matrix}\right.

4.3代码分析

void LCSLength()
{
    for(int i = 1; i <= m; ++i) c[i][0] = 0;//存放各个子问题的最优值
    for(int j = 1; j <= n; ++j) b[0][j] = 0;//存放各个子问题最优值的来源
    for(int i = 1; i < m; ++i)
    {
        for(int j = 1; i <= n; ++j)
        {
            if(x[i]==y[j])
            {
                c[i][j] = c[i-1][j-1] + 1;
                b[i][j] = 1;
            }
            else if(c[i-1][j] >= c[i][j-1])
            {
                c[i-1] = c[i-1][j];
                b[i][j] = 3;
            }
            else 
            {
                c[i][j] = c[i][j-1];
                b[i][j] = 2;
            }
        }
    }
}
                

5.图像压缩问题

        图像的变位压缩存储格式将所给的像素点序列\{p_1,p_2,...p_n\},0\leqslant p_i\leqslant 255分割m个连续段S_1,S_2,...,S_m。第i个像素段S_i(1\leqslant i\leqslant m)中,有l[i]个像素,且该段中每个像素都只用b[i]位表示。设t[i]=\sum_{k=1}^{i-1}l[k],则第i个像素段S_i

                                                      S_i=\{p_{t[i]+1},...,p_{t[i]+l[i]}\} ~~~1\leqslant i\leqslant m

h_i=[log(max(p_k)+1)],则h_i\leqslant b[i]\leqslant 8。因此需要用3位表示b[i],如果限制1\leqslant l[i]\leqslant 255,则需要用8位来表示b[i]。因此第i个像素段所需要的存储空间为l[i]*b[i]+11位。按此格式存储像素序列\{p_1,p_2,...p_n\},则需要\sum_{i=1}^{m}l[i]*b[i]+11m位的空间。

        图像压缩问题要求确定像素序列\{p_1,p_2,...p_n\}的最优分段,使得依此分段所需要的存储空间最少。每个分段的长度不超过255位。

5.1最优子结构分析

        设l[i]b[i](1\leqslant i\leqslant m)\{p_1,p_2,...p_n\}的最优分段。显而易见,l[1]b[1]\{p_1,...p_{l[1]}\}的最优分段。图像压缩问题满足最优子结构性质。

        设s[i]1\leqslant i\leqslant n,是像素序列\{p_1,...,p_i\}的最优分段所需的存储位数。

                                        s[i]=min\{s[i-k]+k*bmax(i-k+1,i)\}+11

其中bmax(i,j)=log(max(p_k)+1)

5.2代码分析

void Compress(int n, int p[], int s[], int l[], int b[]) {
    const int Lmax = 256;
    const int header = 11;
    s[0] = 0;
    for (int i = 1; i <= n; i++) {
        b[i] = length(p[i]); // 计算像素点 p[i] 需要的存储位数
        int bmax = b[i];
        s[i] = s[i - 1] + bmax; // 赋初值
        l[i] = 1;
        for (int k = 2; k <= i && k <= Lmax; k++) {
            if (bmax < b[i - k + 1]) {
                bmax = b[i - k + 1];
            }
            if (s[i] > s[i - k] + k * bmax) {
                s[i] = s[i - k] + k * bmax;
                l[i] = k;
            }
        }
        s[i] += header; // 添加头部信息的开销
    }
}

6.凸多边形最优三角剖分

        凸多边形:一个简单多边形及其内部构成一个闭凸集时,称该简单多边形为凸多边形,即凸多边形边界上或内部的任意两点所连成的直线段上所有点均在凸多边形的内部或边界上。

        为方便描述,用多边形顶点的逆时针序列表示凸多边形,即p=\{v_0,v_1,...v_n\},表示具有n+1条边的凸多边形。  

        若v_iv_j是多边形上的不相邻的两个顶点,则线段v_iv_j称为多边形的一条弦。弦将多边形分割成两个多边形\{v_i,v_{i+1},...v_j\}\{v_j,v_{j+1},...v_i\}

        多边形的三角剖分是将多边形分割成互不相交的三角形的弦的集合T。

        凸多边形的最优三角剖分:给定凸多边形P,以及定义在由多边形的边和弦组成的三角形上的权函数w,要求确定该凸多边形的三角剖分,使得该三角剖分中诸三角形上权之和为最小。

6.1最优子结构分析

        假设存在一个凸多边形的最优剖分P,它的一个子凸多边形Q不是最优剖分。也就是说存在一个代价更小的三角剖分Q^{'}。如果是这样的话,使用Q^{'}替换Q,在保证其它子三角剖分不变的情况下,会产生一个新的整体三角剖分P^{'},它的代价更小,则与P是最优三角剖分的假设矛盾。所以,凸多边形的最优三角剖分具有最优子结构性。

6.2递归关系建立

        定义t[i][j]1\leqslant i\leqslant j\leqslant n为凸子多边形\{v_{i-1},v_i,...,v_j\}的最优三角剖分所对应的权函数值,取其最优值。为方便起见,退化的多边形\{v_{i-1},v_i\}具有权值0。根据此定义,要凸多边形(n+1)P的最有权值为t[1][n]

        t[i][j]的值可以利用最优子结构性质递归地计算。当j-i\geqslant > 1时,凸子多边形至少有3个顶点。由最优子结构性质,t[i][j]的值应为t[i][k]+t[k+1][j]+v_{i-1}v_kv_jv_{i-1}v_kv_j代表该三角形的权值,其中(i\leqslant k \leqslant j-1)。因此

                t[i][j]=\left\{\begin{matrix} 0~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~i=j\\ min\{t[i][k]+t[k+1][j]+w(v_{i-1}v_kv_j)\}~~~~~~i<j\end{matrix}\right.

6.3代码分析

        

void MinWeightTriangulation(int *weights, int n) {
    int t[N][N] = {0}; // 用于存储子问题的最优解
    int s[N][N] = {0}; // 用于存储分割点
    // 初始化
    for (int i = 1; i < n; i++) {
        t[i][i] = 0;
    }
    // 动态规划计算
    for (int r = 2; r < n; r++) {
        for (int i = 1; i < n - r + 1; i++) {
            int j = i + r - 1;
            t[i][j] = t[i + 1][j] + get_weight(i - 1, i, j, weights);
            s[i][j] = i;

            // 尝试所有分割点
            for (int k = i + 1; k < j; k++) {
                int u = t[i][k] + t[k + 1][j] + get_weight(i - 1, k, j, weights);
                if (u < t[i][j]) {
                    t[i][j] = u;
                    s[i][j] = k;
                }
            }
        }
    }
}

7.0-1背包问题 

        给定n个物品和1个背包。物品i的重量是w_i,其价值为v_i,背包的容量为W。如何选择装入背包的物品,使得装入背包中物品的总价值最大? 通常称物体不可分割的背包问题为0-1背包问题。

        问题的形式化描述为,给定W>0w_i>0v_i>01\leqslant i\leqslant n,要求找出n元0-1向量(x_1,x_2,...,x_n),满足:

                                                        max\sum_{i=1}^{n}v_ix_i

                                                        \left\{\begin{matrix} \sum_{i=1}^{n}w_ix_i\leqslant W\\ x_i\in \{0,1\},1\leqslant i\leqslant n \end{matrix}\right.

7.1最优子结构性分析  

        假设(x_1,x_2,...,x_n)是所给0-1背包问题的已给最优解,则(x_2,...,x_n)是下面相应子问题的一个最优解:

                                                        max\sum_{i=2}^{n}v_ix_i

                                                        \left\{\begin{matrix} \sum_{i=2}^{n}w_ix_i\leqslant W-w_1x_1\\ x_i\in \{0,1\},2\leqslant i\leqslant n \end{matrix}\right.

7.2递归关系建立

        令C[i][j]表示子问题\left\{\begin{matrix} \sum_{k=1}^{i}w_kx_k\leqslant j\\ x_k\in \{0,1\} ~~~~~1\leqslant k\leqslant i \end{matrix}\right.的最优解。C[i-1][j-w_ix_i]表示该问题的子问题\left\{\begin{matrix} \sum_{k=1}^{i-1}w_kx_k\leqslant j-w_ix_i\\ x_k,x_k\in \{0,1\} ~~~~~1\leqslant k\leqslant i-1 \end{matrix}\right.的最优解。        

        最优解的递归关系式为:

                                C[i][0]=c[0][j]=0, ~~1\leqslant i\leqslant n,~~1\leqslant j\leqslant W

                                C[i][j]=\left\{\begin{matrix} C[i-1][j]~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~j<w_i\\ max\{C[i-1][j],C[i-1][j-w_i]+v_i\}~~~j\geqslant w_i \end{matrix}\right.

7.3代码分析

void knapsack(int W, int* p, int *w, int size)
{
    int C[size][W];//用于存储子问题的最优解
    for(int i = 0; i < size; ++i)
        for(int j = 0; j < W; ++j)
            C[i][j] = 0;
    for(int i = 1; i < size; ++i)
        for(int j = 1; j < W; ++j)
        {

            if(w[i-1] < j)
            {
                C[i][j]=max(C[i-1][j],C[i-1][j-w[i-1]+p[i-1]);
            }

            else C[i][j] = C[i-1][j];
        }
}
        

8.最优二叉查找树

        给定n个关键字组成的有序序列S=\{s_1,s_2,...,s_n\},用这些关键字构造一棵二叉查找树 T,该树具有性质:存储于每个节点的元素大于左子树中任一个节点中的元素,小于其右子树中任意节点的元素。

        通常用平均比较次数来作为衡量不同二叉查找树查找效率的标准。设在表示为S=\{s_1,s_2,...,s_n\}的二叉查找树T中,元素s_i的结点深度为c_i(1\leqslant i\leqslant n),查找概率为p_i;虚节点为\{e_0,e_1,...,e_n\}e_j的结点深度为d_j,查找概率为q_j(0\leqslant j\leqslant n)。那么平均比较次数通常被定义为:

                                        C=\sum_{i=1}^{n}p_i(1+c_i)+\sum_{j=0}^{n}q_jd_j

        最优二叉查找树是在所有表示有序序列的二叉查找树中,具有最小平均比较次数的二叉树。

8.1最优子结构分析

        将由实结点\{s_1,s_2,...,s_n\}和虚结点\{e_0,e_1,...,e_n\}构成的二叉查找树记为T(1,n)。设定元素s_k作为该树的根结点,1\leqslant k\leqslant n。则二叉查找树T(1,n)的左子树由实结点\{s_1,s_2,...,s_{k-1}\}和虚结点\{e_0,e_1,...,e_k-1\}组成,记为T(1,k-1),而右子树由实结点\{s_{k-1},...,s_{n}\}和虚结点\{e_{k-1},...,e_{n}\}组成,记为T(k+1,n) 。

        如果T(1,n)是最优二叉查找树,假设它的左子树不是一个最优二叉查找树,也就是说存在另一个二叉查找树有更小的查找次数,那么在右子树不变的情况下,拥有该左子树的二叉查找树的效率比原树更高,那么原树就不是最优二叉查找树。则左子树T(1,k-1)和右子树T(k+1,n)也是最优二叉查找树。

8.2递归关系建立

        设T(1,n)的一棵由实结点\{s_1,s_2,...,s_j\}和虚节点\{e_0,e_1,...,e_j\}构成的最优二叉查找子树为T(i,j),则C'^{}[i][j]表示 T[i][j]的平均比较次数。选定结点作为T[i][j]的根结点,则左子树为T(i,k-1),右子树T(k+1,j),相应的比较次数分别为C'[i][k-1]C'[k+1][j]。用p[n]表示查找实结点的概率,用q[n]表示需节点的查找概率。

                        w_{ij}C'[i][j]=w_{i(k-1)}C'i][k-1]+w_{(k+1)j}C'[k+1][j]+w_{ij}

其中w_{ij}=\sum_{m=i}^{j}p_m+\sum_{i=i-1}^{j}q_t~~~~~1\leqslant i\leqslant j\leqslant n

C[i][j]=w_{ij}C'[i][j]

得到:C[i][j]=w_{ij}+min\{C[i][k-1]+C[k+1][j]\}

其中w_{ij}=w_{i(j-1)}+p_j+q_j

8.3代码分析

#include <iostream>
#include <vector>
#include <climits>

using namespace std;

void build_optimal_bst(vector<int> s, vector<double> p, vector<double> q) {
    int n = s.size();
    
    // 初始化 C 和 R 数组
    vector<vector<double>> C(n + 1, vector<double>(n + 1, 0));
    vector<vector<int>> R(n + 1, vector<int>(n + 1, 0));
    
    // 计算 W 数组
    vector<vector<double>> W(n + 1, vector<double>(n + 1, 0));
    for (int i = 1; i <= n; ++i) {
        W[i][i - 1] = q[i - 1];
    }
    
    // 动态规划填充 C 和 R 数组
    for (int l = 1; l <= n; ++l) {  // 子树长度从1到n
        for (int i = 0; i <= n - l; ++i) {
            int j = i + l;
            C[i][j] = numeric_limits<double>::max();
            for (int r = i; r < j; ++r) {
                double t = W[i][j] + C[i][r] + C[r + 1][j];
                if (t < C[i][j]) {
                    C[i][j] = t;
                    R[i][j] = r;
                }
            }
        }
    }
    
    // 更新 W 数组
    for (int l = 1; l <= n; ++l) {
        for (int i = 0; i <= n - l; ++i) {
            int j = i + l;
            W[i][j] = W[i][j - 1] + p[j] + q[j + 1];
        }
    }

    cout << "Cost matrix C:" << endl;
    for (int i = 0; i <= n; ++i) {
        for (int j = 0; j <= n; ++j) {
            cout << C[i][j] << " ";
        }
        cout << endl;
    }
    
    cout << "\nRoot position matrix R:" << endl;
    for (int i = 0; i <= n; ++i) {
        for (int j = 0; j <= n; ++j) {
            cout << R[i][j] << " ";
        }
        cout << endl;
    }
}

int main() {
    vector<int> s = {1, 3, 5, 7};  // 有序序列 S
    vector<double> p = {0.15, 0.1, 0.25, 0.1};  // 查找概率 p
    vector<double> q = {0.05, 0.15, 0.1, 0.15, 0.05};  // 边界及间隙概率 q

    // 构建 OBST
    build_optimal_bst(s, p, q);

    return 0;
}

        

        

        

        

        

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2223680.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

UniHttp 框架,请求http接口

项目案例下载地址: https://download.csdn.net/download/jinhuding/89902024 1.快速开始 2.1引入依赖 <dependency><groupId>io.github.burukeyou</groupId><artifactId>uniapi-http

react18中使用redux管理公共数据仓库实现数据immutable更新

Immutable.js出自Facebook&#xff0c;是最流行的不可变数据结构的实现之一。它实现了完全的持久化数据结构&#xff0c;使用结构共享。所有的更新操作都会返回新的值&#xff0c;但是在内部结构是共享的&#xff0c;来减少内存占用。Immutablejs官网 在上一篇介绍redux的文章&…

Docker 部署 JDK11 图文并茂简单易懂

部署 JDK11 ( Docker ) [Step 1] : 下载JDK11 - JDK 11 | Oracle 甲骨文官网 [Step 2] : jdk11上传服务器/root/jdk11 可自行创建文件夹 进入目录 /root/jdk11 解压文件 tar -zxvf jdk-11.0.22_linux-x64_bin.tar.gz解压后 进入 /root/jdk11/jdk-11.0.22 创建 jre 文件 ./bi…

基于RK3588/算能BM1684 AI盒子:综合视频智能AI分析系统建设方案(三)安全帽、睡岗检测、电瓶车、吸烟场景

安全帽反光衣检测算法 功能说明 安全帽反光衣检测是指在监控场景中预先设定监测区域&#xff0c;在区域内人员没有穿戴安全帽反光衣的现象&#xff0c;及时触发告警。检测目标在1080p图像中的分辨率大小不小于30*30像素。 推荐场景 场景要求&#xff1a;可室内外使用&#x…

VS无法安装Win10SDK_10.0.2200,快捷方法

Visual Studio无法安装Win10SDK_10.0.2200&#xff0c;我在安装VS2019、2022提示&#xff0c;软件就不能编译。 因为之前安装过VS软件&#xff0c;重新安装软件提示“无法安装”。 原因 之前安装在D盘&#xff0c;现在没有D盘了 说明 因为电脑第一次安装VS&#xff0c;会自动安…

安全见闻---清风

注&#xff1a;本文章源于泷羽SEC&#xff0c;如有侵权请联系我&#xff0c;违规必删 学习请认准泷羽SEC学习视频:https://space.bilibili.com/350329294 安全见闻1 泷哥语录&#xff1a;安全领域什么都有&#xff0c;不要被表象所迷惑&#xff0c;无论技术也好还是其他方面…

Python爬虫教程:从入门到精通

Python爬虫教程&#xff1a;从入门到精通 前言 在信息爆炸的时代&#xff0c;数据是最宝贵的资源之一。Python作为一种简洁而强大的编程语言&#xff0c;因其丰富的库和框架&#xff0c;成为了数据爬取的首选工具。本文将带您深入了解Python爬虫的基本概念、实用技巧以及应用…

若依RuoYi-Vue 定时任务 速学

1.若依定时任务模块&#xff08;ruoyi-quartz&#xff09; 那么从一个简单的入门示例开始&#xff0c;掌握定时任务的使用吧&#xff01; 2. 入门示例&#xff08;学会制作一个简单定时任务&#xff09; 首先打开定时任务模块中的task包&#xff0c;这里已经有一个已经写好的R…

51单片机——OLED显示图片

取模软件&#xff1a;链接:https://pan.baidu.com/s/1UcrbS7nU4bsawNxsaaULfQ 提取码:gclc 1、如果图片大小和格式不合适&#xff0c;可以先用Img2Lcd软件进行调整图片大小&#xff0c;一般取模软件使用的是.bmp图片&#xff0c;可以进行输出.bmp格式。软件界面如下&#xff1…

【Javaee】网络原理—TCP协议的核心机制

前言 TCP/IP五层协议是互联网中的主流模型&#xff0c;为网络通信提供了一个稳固的框架。 主要包含了应用层&#xff0c;传输层&#xff0c;网络层&#xff0c;数据链路层&#xff0c;物理层。 本篇主要介绍传输层的TCP协议的核心机制 一. 确认应答&#xff08;ack&#xf…

ESP32-S3学习笔记:常用的ESP-IDF命令总结

参考资料&#xff1a;1.esptool.py工具 2.idf.py工具 后续文章的讲解需要用到IDF命令行工具&#xff0c;当前文章简单介绍一下。 目录 打开命令行的小技巧 一、读flash信息 二、擦除flash 三、读flash数据 四、写flash数据 打开命令行的小技巧 大家安装完IDF开发包后…

关于小程序审核需要提交订单列表页面path的修改办法

小程序又又又又又搞事情啦&#xff5e;&#xff5e;&#xff5e; 从12月31号起&#xff0c;所有有订单生成逻辑的小程序在审核过程中&#xff0c;必须要填写订单列表页面的path才可以进行审核 在代码层面上会有一些小的改动&#xff0c;下面就告诉大家怎么去修改吧。 第一步…

算法笔记day07

目录 1.最长回文子串 2.买卖股票的最好时机(一) 3.过河卒 1.最长回文子串 最长回文子串_牛客题霸_牛客网 算法思路&#xff1a; 使用中心扩散算法&#xff0c;枚举所有的中点&#xff0c;向两边扩散&#xff0c;一个中点需要枚举两次&#xff0c;一次当回文串是奇数另一次回…

SpringBoot实现 License 认证(只校验有效期)

文章目录 一、License介绍二、授权者生成密钥对三、授权者生成license.lic证书3.1、 配置pom.xml3.2 、License生成类3.3 、License生成类需要的参数类3.4、自定义KeyStoreParam3.5、main方法生成license.lic注意事项 四、使用者配置4.1、配置pom.xml4.2、License校验类4.3、Li…

论文速读:YOLO-G,用于跨域目标检测的改进YOLO(Plos One 2023)

原文标题&#xff1a;YOLO-G: Improved YOLO for cross-domain object detection 中文标题&#xff1a;YOLO-G&#xff1a;用于跨域目标检测的改进YOLO 论文地址&#xff1a; 百度网盘 请输入提取码 提取码&#xff1a;z8h7 代码地址&#xff1a; GitHub - airy975924806/yolo…

ArkUI自定义TabBar组件

在ArkUI中的Tabs&#xff0c;通过页签进行内容视图切换的容器组件&#xff0c;每个页签对应一个内容视图。其中内容是图TabContent作为Tabs的自组件&#xff0c;通过给TabContent设置tabBar属性来自定义导航栏样式。现在我们就根据UI设计的效果图来实现下图效果&#xff1a; 根…

打包方式-jar和war的区别

1、jar包 JAR包是类的归档文件&#xff0c;与平台无关的文件格式&#xff0c;其实jar包就是java的类进行编译生成的class文件进行打包的压缩包。 JAR以ZIP文件格式为基础&#xff0c;与ZIP不同的是&#xff0c;JAR不仅用于压缩和发布&#xff0c;还用于部署和封装库、组件和插…

数据结构 ——— 数组栈oj题:有效括号

目录 题目要求 代码实现 题目要求 给定一个只包括 (&#xff0c;)&#xff0c;{&#xff0c;}&#xff0c;[&#xff0c;] 的字符串 s &#xff0c;判断字符串是否有效 有效字符串需满足&#xff1a; 左括号必须用相同类型的右括号闭合。 左括号必须以正确的顺序闭合。 每…

【p2p、分布式,区块链笔记 Blockchain】truffle001 以太坊开发框架truffle初步实践

以下是通过truffle框架将智能合约部署到Ganache的步骤 Truffle简介环境准备&#xff1a;智能合约 编写 & 编译部署合约本地服务器ganache配置网络配置部署合约: 运行Truffle迁移&#xff08;部署&#xff09;:与智能合约交互: 以下是通过truffle框架将智能合约部署到Ganach…

kaggle在线训练深度学习模型

kaggle https://www.kaggle.com/ code 通过jupyter notebook上传代码&#xff0c;执行训练 dataset 支持手动上传本地资源文件到input /kaggle/input&#xff0c;dataset通过input访问&#xff0c;input目录是只读的 /kaggle/working&#xff0c;保存文件到working&#…