动态规划方法--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", n, cap, dp[n][cap]);

    for(j=0; j<cap+1; ++j)
    {
        if(j==0) printf("\t     ");
        printf("%d\t", j);
    }
    printf("\n");

    for(i=0; i<n+1; ++i)
    {
        if(i==0) printf("\t   %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\t", i, j, dp[i][j]);
            //if(j==0) printf("\t");
            printf("%d\t", dp[i][j]);
        }
        printf("\n");
    }
    
    printf("\n");
//=============================================================
    //逆序倒推求得命中的数据
    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\n", 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\n", 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("\n\n");
 53     int q=0, p=0;
 54     for(q=0; q<len2; ++q)
 55     {
 56         if(q==0) printf(" \t");
 57 
 58         printf("%s\t", vSubStr[q].c_str());
 59     }
 60     printf("\n");
 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\t", q, p, dp[q][p]);
 69             printf("%d\t", dp[q][p]);
 70         }
 71         printf("\n");
 72     }
 73     printf("\n\n");
 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\n", 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\n", 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 }

 

  运行结果:

  

 

posted @ 2017-10-11 15:46  波妞妈  阅读(382)  评论(0编辑  收藏  举报