zoukankan      html  css  js  c++  java
  • 动态规划算法最长公共子序列和最优二叉查找树

    一、         最长公共子序列

    1. 问题

    通过比较公共子序列的长度来判断两个序列是否相似,如ABCBCDA和ACBCCAD的最长公共子序列为ABCCD。

    现在有两个序列,找出X和Y的最长公共子序列(LCS)。

    2. 子问题拆分

    这个问题就是求所有公共序列,然后找出最长的那个,而如何计算序列的长度,就是一个递归的过程了,公式如下:

        这个问题是几个动态规划问题中最好理解的一个,是一个二维遍历标记的过程,最后再遍历一遍标记就能获得结果,不再赘述,直接看伪代码。

    3. 伪代码

    LCS-LENGTH(X, Y)
        m <- length[X]
        n <- length[Y]
        for i <- 1 to m
            c[i, 0] <- 0
        for j <- 0 to n
            c[0, j] <- 0
        
        for i <- 1 to m
            for j <- 1 to n
                if x[i] = y[i]
                    c[i, j] = c[i-1, j-1] + 1
                    b[i, j] <- "\"
                else if c[i-1, j] >= c[i, j-1]
                    c[i, j] = c[i-1, j]
                    b[i, j] <-"|"
                else
                    c[i, j] = c[i, j-1]
                    b[i, j] <- "-"
    return b and c
    
    PRINT-LCS(b, X, i, j)
        n = 0
        l = 0
        for k <- j downto 1
            if n[i, k] > n
                l = k    
        while(i != 0 and l != 0)
            if b[i, l] = "\"
                print X[i]
                i = i - 1
                l = l - 1
            else if b[i, j] = "|"
                i = i - 1
            else
                l = l - 1

    4.代码

    二、         最优二叉查找树

    一篇包含很多文字的文章,遍历整篇文章,在一棵包含所有文字(包括文章内的和文章外的字)的二叉树中查找对应的字,计算出总的查找次数,最优二叉查找树就是为了获取总查找最少次数(也就是求查找次数的期望值最小)这类问题而设计的算法。

    1. 问题

    假设一个文章有s个文字,其中共有n个不同的文字,且已经按拼音排序 ,每个文字搜索的概率为,由于有某些文字不在该篇文章中,所以还会有n+1个虚拟文字 ,对应的概率为代表所有小于的值,表示所有大于的值,所有的概率: ,一次搜索的期望代价为

    表:每个文字对应的概率

    i

    0

    1

    2

    3

    4

    5

    pi

    0.15

    0.10

    0.05

    0.10

    0.20

    qi

    0.05

    0.10

    0.05

    0.05

    0.05

    0.10

    2. 子问题拆分

    使用子结构的最优解来构造原问题的最优解。

    的概率总和,那么的期望代价为

    进一步计算出:

    最终推导出递归公式:

    3. 伪代码

    OPTIONAL-BST(p, q, n)
        for i <- 1 to n + 1
            e[i, i - 1] <- q[i - 1]
            w[i, i - 1] <- q[i - 1]
        
        for l <- 1 to n
            for i <- 1 to n - l + 1
                j = i + l - 1
                e[i, j] <- 0X7FFFFFFF
                w[i, j] <- w[i, j - 1] + p[j] + q[j]
    
                for r <- i to j
                    t <- e[i, r - 1] + w[i, j] + e[r + 1, j]
                    if t < e[i, j]
                        e[i, j] <- t
                        root[i, j] <- r
    return e and root
    PRINT-BST(root, n, i, j)
        if j = n and i = 1
            print "root is " root[i, j]
        if i < j
            if i <= root[i,j] - 1
                print "k" root[i, root[i,j]-1] "is left of " root[i,j]
                PRINT-BST(root, n, i, root[i,j]-1)
            if root[i,j] + 1<= j
                print "k" root[root[i,j]+1, j] "is right of " root[i,j]
                PRINT-BST(root, n, root[i,j]+1, j)
        else if i = j    //叶子节点
            print "d" root[i, j] - 1 "is left of" root[i, j]
            print "d" root[i, j] "is right of" root[i, j]
        else            //k仅有一个左子节点为k时
            print "d" j "is right of k" j

    说明:PRINT-BST有点复杂,在此稍加讲解,当打印整个树时,i应该为1,而j应该为n,即树的实际节点数(不包括虚拟节点)。root[i,j]表示在 到 之间的根,其中的值代表k的下表,我们通过OPTIONAL-BST获得的root数组:

    j=1

    j=2

    j=3

    j=4

    j=5

    i=1

    1

    1

    2

    2

    4

    i=2

    2

    2

    2

    4

    i=3

    3

    4

    5

    i=4

    4

    5

    i=5

    5

    第一次进入时,i=1,j=5,root[i,j]=2,说明在 到 之间k2为根,然后再找i到2-1之间的根作为根的左节点,同样,2+1到j的根作为根的右节点,然后递归计算,就能获取到树的结构了。

    4. 代码

    #include <iostream>
    using namespace std;
    #define MAX 65536
    
    double p[6] = {0.00,0.15,0.10,0.05,0.10,0.20};
    double q[6] = {0.05,0.10,0.05,0.05,0.05,0.10};
    void optimal_bst(float e[][6],int root[][6],float w[][6],int n)
    {
        int i = 0,j = 0;
    
        for (i = 1; i <= n + 1; i++)
        {
            e[i][i - 1] = q[i - 1];
            w[i][i - 1] = q[i - 1];
        }
        int l = 0;
        for (l = 1; l <= n; l++)
        {
            for (i = 1; i <= n - l + 1; i++)
            {
                j = i + l - 1;
                e[i][j] = MAX;
                w[i][j] = w[i][j - 1] + p[j] + q[j];
                for (int r = i; r <= j; r++)
                {
                    double t = 0;
                    t = e[i][r - 1] + e[r + 1][j] + w[i][j];
                    if (t < e[i][j])
                    {
                        e[i][j]= t;
                        root[i][j] = r;
                    }
                }    
            }
        }  
    }
    
    void print_bst(int root[][6], int n, int i, int j)
    {
        int newI, newJ;
        if((i == 1)&&(j == n))
            cout<<"K["<<root[i][j]<<"]"<<" is the root"<<endl;
         if (i < j)
         {
            newI = root[i][j] - 1;
            newJ = root[i][j] + 1;
              if(i <= newI)
                   cout<<"k["<<root[i][newI]<<"]"<<" is the left child of k["<<root[i][j]<<"]"<<endl;
              print_bst(root,n, i,root[i][j] - 1);
              if(newJ <= j)
                   cout<<"K["<<root[newJ][j]<<"]"<<" is the right child of K["<<root[i][j]<<"]"<<endl;
              print_bst(root,n, root[i][j] + 1,j);
           }
         if (i == j)
         {
              cout<<"d["<<i - 1<<"]"<<" is left child of k["<<i<<"]"<<endl;
              cout<<"d["<<i<<"]"<<" is right child of k["<<i<<"]"<<endl;
         }
         if(i > j)
              cout<<"d["<<j<<"]"<<" is right child of k["<<j<<"]"<<endl;
    }
    
    int main()
    {
        float e[7][6];
        int root[6][6];
        float w[6][6];
        memset(e, 0, 7*6*sizeof(float));
        memset(w, 0, 6*6*sizeof(float));
        memset(root, 0, 6*6*sizeof(int));
    
        optimal_bst(e,root,w,5);
        for(int i = 1; i <= 5; i++){
            for(int j= 1; j <= 5; j++)
                cout<<root[i][j]<<" ";
            cout<<endl;
        }
        cout<<endl;
        for(int i = 1; i <= 6; i++){
            for(int j= 0; j <= 5; j++)
                cout<<e[i][j]<<" ";
            cout<<endl;
        }
        cout<<endl;
        print_bst(root,5,1,5);
        return 0;
    }
  • 相关阅读:
    剑指offer题解(python版)(更新到第16题)
    Java基础知识详解:值传递
    [LeetCode] 583. Delete Operation for Two Strings
    [LeetCode] 856. Score of Parentheses
    [LeetCode] 1129. Shortest Path with Alternating Colors
    [LeetCode] 1561. Maximum Number of Coins You Can Get
    [LeetCode] 1052. Grumpy Bookstore Owner
    [LeetCode] 991. Broken Calculator
    [LeetCode] 1054. Distant Barcodes
    [LeetCode] 1245. Tree Diameter
  • 原文地址:https://www.cnblogs.com/geekma/p/2591730.html
Copyright © 2011-2022 走看看