动态规划---01背包与记忆化搜索

        动态规划是一种高效的算法。在数学和计算机科学中,是一种将复杂问题的分成多个简单的小问题思想 ----  分而治之。因此我们使用动态规划的时候,原问题必须是重叠的子问题。运用动态规划设计的算法比一般朴素算法高效很多,因为动态规划不会重复计算已经计算过的子问题。因为动态规划又可以称为“记忆化搜索”。

        01背包是介绍动态规划最经典的例子,同时也是最简单的一个。我们先看看01背包的是什么?

问题(01背包):
        有n个重量和价值分别为Wi和Vi的物品。从这些物品中挑出总重量不超过W的物品,求所有挑选方案中价值总和的最大值。

       这就是被称为01背包的问题。在没学习动态规划之前,我们看到这个问题第一反应会用dfs搜索一遍。那我们先使用这种方法来求解01背包问题:

//n,W 如题意所述
//这里的MAXN表示W,n中的最大值(个人习惯)
//如果不习惯这种偷懒的方式
//可以使用MAXW和MAXN分别表示W和n的最大值
int W, n;
//w[i]和v[i]分别表示Wi,Vi
int w[MAXN], v[MAXN];
//从第i个物品开始挑选总重量小于j的部分
int dfs(int i, int j){
    int res;
    //已经没有剩余物品
    if(i == n) res = 0;
    //无法挑选第i个物品
    else if(j < w[i]) res = dfs(i+1, j);
    //比较挑和不挑的情况,选取最大的情况
    else res = max(dfs(i+1, j), dfs(i+1, j-w[i])+v[i]);
    return res;
}

         乍一看dfs好像就可以解决这个问题,那还有动态规划什么事。然而我们仔细分析一下时间复杂度,每一种状态都用选或者不选两种可能。所以我们可以得出使用dfs的时间复杂度为O(2^n)。显然这个方法不是一个很好的方法,因为这个时间复杂度太高了。我们仔细研究可以发现,造成时间复杂度这么高的原因是重复计算。既然我们找到复杂度这么高的原因,那我们就可以想办法减少它重复计算的次数。仔细分析容易想到,使用一个二维数组来记录每一次搜索的答案,这样我们就避免了重复计算。

//n,W 如题意所述
int W, n;
//w[i]和v[i]分别表示Wi,Vi
int w[MAXN], v[MAXN];
//保存每一次搜索的答案
//初始化dp数组的值,使其全为-1
int dp[MAXN][MAXN];
//从第i个物品开始挑选总重量小于j的部分
int dfs(int i, int j){
    if(dp[i][j] >= 0) return dp[i][j];
    int res;
    //已经没有剩余物品
    if(i == n) res = 0;
    //无法挑选第i个物品
    else if(j < w[i]) res = dfs(i+1, j);
    //比较挑和不挑的情况,选取最大的情况
    else res = max(dfs(i+1, j), dfs(i+1, j-w[i])+v[i]);
    //将结果记录在dp数组中
    return dp[i][j] = res;
}



        这样的小技巧,我们称之为记忆化搜索。我们只是小小的改变就让它的时间复杂度降低至O(nW)。

        仔细分析,可以发现我们还可以有更简单的写法:

//dp[i+1][j] 表示从前i个物品挑选出总重量超过j的物品时,背包中的最大价值
void solve(){
    //还没开始挑选的时候,背包里的总价值为0
    for(int j = 0; j <= W; j++) dp[0][j] = 0;
    for(int i = 0; i < n; i++){
        for(int j = 0; j <= W; j++){
            if(j < w[i]) dp[i+1][j] = dp[i][j];
            else dp[i+1][j] = max(dp[i+1], dp[j-w[i]]+v[i]);
        }
    }
}

        使用递推方程直接求解的方法,我们称之为dp。因为他每一次的选取,都在动态的计算最优的情况。当然可能他局部不是最优,但是整体一定是最优解。这就是他和贪心算法最大的不同,贪心算法,每一次都是最优,但是整体不一定不是最优。

附上一道习题:hdu2602Bone Collector

  • 3
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值