zoukankan      html  css  js  c++  java
  • 浅谈动态规划

    1 动态规划的概念:
              把问题转变成状态(计算机的本质就是一个状态机,内存里的各种数据构成了当前的状态,CPU只能利用当前的状态去计算下一个状态),并且将状态作为缓存进行存储,当求第 i 个阶段的最优解时,可由前 i-1 个阶段的最优解得到。动态规划的方程是:
                     
    2 动态规划的理解: 
         先“记忆”之前的某些事情(状态),在求最终目标(最优状态)时,直接使用直接缓存的“记忆”。(“记忆”通过递归的方式存储)
     
    3 动态规划的特点:
    • 最优子结构:当子问题最优时,母问题通过一定的选择判断就一定能有最优解的情况。
    • 子问题重叠:当母问题和子问题本质上是一个问题的情况。(子问题之间的参数传递是重点!也就是状态转移方程)
    • 边界:当某个子问题不再需要提出子子问题就可以得到答案的情况,这个答案就是边界。
     
    4 动态规划和分治的共同点和不同点:
    • 共同点:都是把原问题划为若干个子问题,求得子问题的解后,再把子问题的解组合来求得原问题的解。
    • 不同点
      • 分治用递归的方式求解子问题的解,重复计算子问题解;动态规划则用有记忆功能的递归式(递推),提升了效率。
      • 分治的子问题都是独立的(没有公共子问题);动态规划则允许子问题之间有联系,有交叠。       
    5 解题思路:
              当确定问题具有属性:当前状态和之前的状态有关,即母问题可以由最优子问题得到,且子问题是重叠的。可以确定为动态规划问题。
              如果问题是要维护一张二维表,首先要确定二维矩阵的行和列各代表什么(背包问题中,行表示物品,列表示包承重)
              此时,解题的重点是找到状态转移方程。(说白了要就是递推关系式的确立)
      
    6 背包问题:
            问题阐述    n个重量为w1,w2,w3......wn,价值为v1,v2,v3......vn的物品和一个承重为W的背包,求这些物品的最大价值集(能放入背包中)。
     
            问题分析   设F(i,j)为背包问题的最大价值,即:前i个物品放入承重为j的背包中的最大价值。F(i,j)可以看做是一个子问题,他的解可以由之前的子问题的解组合求出。由第i个物品能不能放入承重为j的背包,可以将F(i,j)的解分为两个类别:包含第i个物品和不包含第i个物品:
                                    当j-wi<0,F(i,j)=F(i-1,j)             ps:j-wi<0表示第i个物品放不进承重为j的背包中
                                    当j-wi>=0,F(i,j)=max( F(i-1,j), vi+F(i-1,j-wi) )     ps:F(i-1,j)不等于F(i-1,j-wi)
             我们的目标就是求得F(n,W)。可以将这个问题看成是二维表的动态规划问题,即:回溯构建一个二维表,将其填满,表的右下角即为问题的解(自底向上的求解方法)
     
            问题求解   采用两种方法,分别是自底向上的求解,以及带有记忆的自顶向下求解。
    • 自底向上的动态规划算法:按子问题从小到大的顺序将子问题的解填充到表中,每个子问题都只求解一次。当表填充完毕后,表的右下角的值就是问题的解。
    int dp_Backpack(struct backpack bp[n], int W)
    {
         int F[n+1][W+1];
         for(int i=0;i<n+1;i++)   
            F[i][0] = 0;
         for(int j=0;j<W+1;j++)   
            F[0][j] = 0;
         for(int i=1;i<n+1;i++)
         {
            for(int j=1;j<W+1;j++)
            {
                if(j<bp[i-1].w)
                    F[i][j] = F[i-1][j];
                else
                    F[i][j] = max( F[i-1][j], bp[i-1].v+F[i-1][j-bp[i-1].w] );
            }
         }   
         return F[n][W];    
    }
    • 带有记忆的自顶向下动态规划算法:自顶向下的方式求解,①除表中0行和0列的值为0外,初始化表中所有格为null(可以设为-1);②一旦需要某个格中的值,先检查该格,若为null,则递归调用进行计算并记录入表,否则直接从表中取值。ps:避免计算不必要的子问题
    struct backpack bp[n] = { //初始化bp结构体数组 }
    int F[n+1][W+1];
    void initF(int F[n+1][W+1])
    {
        for(int i=0;i<n+1;i++)    //0列为0
            F[i][0] = 0;
        for(int j=0;j<W+1;j++)    //0行为0
            F[0][j] = 0;
        for(int i=0;i<n+1;i++)    //剩余表中值初始为-1
            for(int j=0;j<W+1;j++)
                F[i][j] = -1;
    }
    int dp_Backpack(int i, int j)
    {
         if(F[i][j] == -1)            //该值需要递归计算(在表中从上到下的递归计算下来)
         {
             if(j < bp[i-1].w)    
                F[i][j] = dp_Backpack(i-1,j);
             else
                F[i][j] = max( dp_Backpack(i-1,j) , bp[i-1].v+dp_Backpack(i-1,j-bp[i-1].w) );
         }
         return F[i][j];
    }
    initF(F);
    int max_val = dp_Backpack(n,W);
            完整代码   采用以上两种方法求解01动态规划问题:
    #include <iostream>
    using namespace std;
    #define N  4                     //物品总数
    #define C  5                     //背包的承重
    int w[N] = {2,1,3,2};         //物品重量
    int v[N] = {12,10,20,15};  //物品价值
    int CurWeight = 0;           //当前总重量
    int CurValue = 0;              //当前总价值
    int x[N] ={0,0,0};               //当前的背包选取情况(1表示选取,0表示不选取)
    int MaxValue = 0;             //最大价值
    int MaxPack[N] = {0,0,0}; //最大价值下的背包选取情况(1表示选取,0表示不选取)
     
    /*用动态规划解决01背包问题*/
    int dp_Backpack_1(int NN, int W)
    {
         int n = NN+1;
         int c = W+1;
         int F[n][c];
         for(int i=0;i<n;i++)   
            F[i][0] = 0;
         for(int j=0;j<c;j++)   
            F[0][j] = 0;
         for(int i=1;i<n;i++)
         {
            for(int j=1;j<c;j++)
            {
                if(j<w[i-1])
                    F[i][j] = F[i-1][j];
                else
                    F[i][j] = max( F[i-1][j], v[i-1]+F[i-1][j-w[i-1]] );
            }
         }   
         return F[NN][W];    
    }
     
    /*改进的动态规划求01背包问题*/
    void initF(int F[N+1][C+1])
    {
        for(int i=0;i<N+1;i++)
            F[i][0] = 0;
        for(int j=0;j<C+1;j++)
            F[0][j] = 0;
        for(int i=1; i<N+1; i++)
            for(int j=1; j<C+1; j++)
                F[i][j] = -1;
    }
    void printF(int F[N+1][C+1])
    {
        for(int i=0; i<N+1; i++)
        {
            for(int j=0; j<C+1; j++)
                cout<<F[i][j]<<" ";
            cout<<endl;
        }
    }
    int dp_Backpack_2(int F[N+1][C+1], int i, int j)
    {
         if(F[i][j] == -1)        //该值需要递归计算(在表中从上到下的递归计算下来)
         {
             if(j < w[i-1])        
                F[i][j] = dp_Backpack_2(F,i-1,j);
             else
                F[i][j] = max( dp_Backpack_2(F,i-1,j) , v[i-1]+dp_Backpack_2(F,i-1,j-w[i-1]) );
         }
         return F[i][j];
    }
     
    int main()
    {
        cout<<"use dp_1's value: "<<dp_Backpack_1(N,C)<<endl;
        int F[N+1][C+1];
        initF(F);
        cout<<"use dp_2's value: "<<dp_Backpack_2(F,N,C)<<endl;
        printF(F);
    }
  • 相关阅读:
    压缩和还原压缩的JS代码
    mysql给某字段随机赋特定范围的整数值
    (五)绘制图像
    (四)旋转 缩放 位移 保存状态
    (三)描边 填充 绘制 开始路径 结束路径 绘制文本
    (二)导出图像
    (一)获取上下文绘图环境
    封装原生Ajax
    原生Ajax写法(GET)
    Angular跨域
  • 原文地址:https://www.cnblogs.com/ladawn/p/8469559.html
Copyright © 2011-2022 走看看