• 动态规划方法--0-1背包问题&最长公共子串&最长公共子序列


    一、动态规划方法—基本概念

      1、解决多阶段决策过程最优化的一种数学方法

      2、1951年美国数学家贝尔曼等人根据多阶段决策问题的特点,把多阶段决策问题变换为一系列互相联系的单阶段问题,然后逐个加以解决,即提出了动态规划这个方法

      3、基本概念:阶段、状态、决策、策略、状态转移方程、指标函数和最优值函数

      4、基本思想:从边界条件开始,逐段递推寻优,在每一个子问题的求解中,均利用了它前面的子问题的最优化结果,依次进行,最后一个子问题所得的最优解,就是整个问题的最优解

      5、基本方程式—难点:从实际问题中求得基本方程式,即抽象出动态规划表dp,dp一般是一个数组,可能是一维的也可能是二维的,也可能是其他的数据结构,以空间换取时间的算法

      6、由于初始状态是已知的,每阶段的决策都是该段状态的函数,故最优策略所经过的各阶段状态便可逐次变换得到,从而确定最优路线

      7、最优化原理,也就是最优子结构性质。这指的是一个最优化策略具有这样的性质,无论过去状态和决策如何,对前面的决策所形成的状态而言,余下的决策必须构成最优策略,简单来说就是一个最优化策略的子策略总是最优的,如果一个问题满足最优化原理,就称其有最优子结构性质。

    二、0-1背包问题

      问:一个背包承重Wkg,有n(1,2......n)件物品。已知第件物品的重量为kg,价值是,每件物品只能选择要装入还是不装入背包,要求在不超过背包承重的前提下,选出的物品总价值最大

      分析:假设表示第x件物品,不超过重量y的时候的最大价值,则第x件物品的情况:

        情况1:如果选择了第x件物品,则前x-1件物品的重量不能超过,此时

        情况2:如果不选择第x件物品,此时

        综上所述:

      代码如下:

    int bag_0_1()
    {
        int n=5;//物品个数
        int cap = 10;//最大承重
        int v[5] = {5,18,23,22,8};//每个物品的价值
        int w[5] = {4,3,7,6,1};//每个物品的重量
    
        int i=0, j=0;
    
        //dp[n+1][cap+1]
        int **dp = new int*[n+1];  
        for(i = 0; i < n+1; i++)
        {
            dp[i] = new int[cap+1];
        }
    
        for(i = 0; i < n+1; i++)
        {
            for(j = 0; j < cap+1; j++)
            {
                dp[i][j]=0;//第0行都初始化为0   
            }
        }
    //==============================================================================    
    
        for (i=1; i <=n; i++)/*枚举物品*/ 
        { 
            for (j=1; j<cap+1; j++)/*枚举重量*/  
            {
                //判断枚举的重量和当前选择的物品重量的关系
                //如果枚举的和总量大于等于选择物品,则需要判断是否选择当前物品 
                if (j-w[i-1]>=0)
                {
                    dp[i][j] = max(dp[i-1][j], dp[i-1][j-w[i-1]]+v[i-1]);
                }  
                else
                {
                    /*如果枚举的重量还没有当前选择物品的重量大,那就只能是不取当前物品*/  
                    dp[i][j] = dp[i-1][j];
                } 
            }  
        }  
    //=============================================================
        printf("dp[%d][%d]=%d
    ", n, cap, dp[n][cap]);
    
        for(j=0; j<cap+1; ++j)
        {
            if(j==0) printf("	     ");
            printf("%d	", j);
        }
        printf("
    ");
    
        for(i=0; i<n+1; ++i)
        {
            if(i==0) printf("	   %d ", i);
            if(i>0)
            {
                printf("w[%d]=%d(%d) %d ", i-1, w[i-1], v[i-1], i);
            }
            for(j=0; j<cap+1; ++j)
            {
                //printf("dp[%d][%d]=%d	", i, j, dp[i][j]);
                //if(j==0) printf("	");
                printf("%d	", dp[i][j]);
            }
            printf("
    ");
        }
        
        printf("
    ");
    //=============================================================
        //逆序倒推求得命中的数据
        i=n;
        j=cap;
        while(i>=1 && j>=1 /*&& j-w[i-1]>=0*/)
        {
            if(j-w[i-1]>=0 && dp[i][j] == dp[i-1][j-w[i-1]]+v[i-1] )
            {
                printf("hit info: w[%d]=%d, v[%d]=%d
    ", i-1, w[i-1], i-1, v[i-1]);
                j=j-w[i-1];
                i=i-1;
            }
            else if(dp[i][j]==dp[i-1][j] || j-w[i-1]<0)
            {
                i--;
            }
            else if(dp[i][j] > dp[i-1][j])
            {
                j--;
            }
            
        }
    
        for(i = 0; i < n+1; i++)         //释放动态申请的二维数组
        {
            delete[] dp[i];
        }
        
        delete[] dp; 
    
        return -1; 
    }

      运行结果如下:

    三、最长公共子序列

      字符串或者序列串的一个子序列是指,其中

      如:字符串abcdef,其中 acf、bd等都是子序列

      问:,求的最长公共子序列??

      设:表示的一个最长公共子序列

        情况1:,则

        情况2:,则

       设:记录序列的字符串长度

       综上所述,公式如下:

        

      代码如下:

      1 int longest_common_subsequence(std::vector<std::string> vMainStr, std::vector<std::string> vSubStr)  
      2 { 
      3     //动态规划算法
      4     int i,j,k,len1,len2,max,x,y;  
      5 
      6     len1 = vMainStr.size();
      7     len2 = vSubStr.size();
      8     
      9     printf("len1=%d, len2=%d
    ", len1, len2);
     10     
     11     int **dp = new int*[len1+1];  
     12     for(i = 0; i < len1+1; i++)
     13     {
     14         dp[i] = new int[len2+1];
     15     }
     16           
     17     for(i = 0; i < len1+1; i++)
     18     {
     19         dp[i][0]=0;//第0列都初始化为0
     20     }
     21         
     22     for(j = 0; j < len2+1; j++)
     23     {
     24         dp[0][j]=0;//第0行都初始化为0   
     25     }
     26 
     27 
     28     max = -1; 
     29     //求dp值
     30     for(i = 1; i < len1+1; i++)//str1
     31     {    
     32         for(j = 1; j < len2+1; j++)//str2 
     33         {  
     34             //subsequence
     35             if(vMainStr[i-1]==vSubStr[j-1])
     36             {
     37                 dp[i][j]=dp[i-1][j-1]+1;
     38             }   
     39             else if(dp[i][j-1] > dp[i-1][j])
     40             {
     41                 dp[i][j]=dp[i][j-1];
     42             }
     43             else
     44             {
     45                 dp[i][j]=dp[i-1][j];
     46             }
     47         }
     48     } 
     49 
     50 //====================================================================================================
     51     //输出dp数组情况
     52     printf("
    
    ");
     53     int q=0, p=0;
     54     for(q=0; q<len2; ++q)
     55     {
     56         if(q==0) printf(" 	");
     57 
     58         printf("%s	", vSubStr[q].c_str());
     59     }
     60     printf("
    ");
     61 
     62     for(q=0; q<len1+1; ++q)
     63     {
     64         if(q==0) printf("   ");
     65         if(q>0) printf("%s  ",vMainStr[q-1].c_str());
     66         for(p=0; p<len2+1; ++p)
     67         {
     68             //printf("dp[%d][%d]=%d	", q, p, dp[q][p]);
     69             printf("%d	", dp[q][p]);
     70         }
     71         printf("
    ");
     72     }
     73     printf("
    
    ");
     74 //====================================================================================================        
     75 
     76     //输出公共子序列
     77     std::string s,sTmp;  
     78     i = len1;
     79     j = len2;
     80     k = dp[i][j];
     81     max = k;
     82     printf("i=%d, j=%d, k=%d
    ", i, j, k);
     83     
     84     while(i>0 & j>0)
     85     {
     86         if(vMainStr[i-1] == vSubStr[j-1] && dp[i][j]==dp[i-1][j-1]+1)
     87         {
     88             sTmp = vMainStr[i-1];
     89             sTmp.append(s);
     90             s = sTmp;
     91             --j;
     92             --i;
     93         }else if(vMainStr[i-1] != vSubStr[j-1] && dp[i-1][j] > dp[i][j-1])//上>左
     94         {
     95             --i;
     96         }
     97         else
     98         {
     99             --j;
    100         }
    101     }
    102     
    103     printf("Longest-Common-subsequence:%s
    ", s.c_str());   
    104 
    105     for(i = 0; i < len1+1; i++)         //释放动态申请的二维数组
    106     {
    107         delete[] dp[i];
    108     }
    109     
    110     delete[] dp;  
    111     
    112     return max;  
    113 }

      运行结果:

      

  • 相关阅读:
    一个小型公司怎么落地微服务
    【操作系统笔记】 02.进程的描述与控制
    shell脚本 PHP+swoole的安装
    Mac sourceTree每次都输入密码
    kafka的安装
    leetcode-剑指56-I-II-OK
    leetcode-剑指13-OK
    leetcode-剑指66-OK
    leetcode-剑指31-OK
    leetcode-剑指26-OK
  • 原文地址:https://www.cnblogs.com/luyanhong456/p/7642897.html
Copyright © 2020-2023  润新知