经典0-1背包问题

三种思路:递归、改进递归(记忆化搜索)、自底向上动态规划

#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;

/*
问题描述:一个背包,容量为C。现在有n种不同的物品,编号为0-n-1,其中每一种
           物品的重量为w(i),价值为v(i)。问如何将物品装入背包使总价值量
           最大。
思考:双约束下的选取组合最优解问题,物品数、背包容量约束的情况下求价值量最大。
      每个物品有取/不取两种策略,故n个物品的策略总数为(2^n)*n,这也是暴力
      解法的时间复杂度。
      常规思路:递归、改进递归(记忆化搜索)、自底向上动态规划
      函数定义:F(n,C)表示在n个物品中选取放入容量为C的背包中。
      选取策略表示:F(i,c)=max(F(i-1,c),v(i)+F(i-1,c-w(i))),即每次都可以
      不选取当前物品or选取当前物品。
*/

/*
思路一:递归。考虑递归出口,显然可选取物品的索引低于下界时本次贡献价值量返回0,
       背包容量不足时本次贡献价值量返回0。
*/
class Knapsack01_1{

private:
    //考虑0-index的物品,填充当前背包容量c,组合的最大值
    int bestValue(const vector<int> &w,const vector<int> v,int index,int c){

        if(index<0||c<=0){ //递归出口
            return 0;
        }
        //尝试向背包中放入物品,两种策略选其优
        //int res=max(bestValue(w,v,index-1,c),v[index]+bestValue(w,v,index-1,c-w[index]));
        //另一种更好的写法:容量足够才考虑另一种策略,避免多余的递归
        int res=bestValue(w,v,index-1,c);
        if(c>=w[index]){
            res=max(res,v[index]+bestValue(w,v,index-1,c-w[index]));
        }
        return res;

    }

public:
    int knapsack01(const vector<int> &w,const vector<int> &v,int C){
        int n=w.size();
        return bestValue(w,v,n-1,C);//递归函数bestValue,考虑0-n-1共n个物品

    }
}; 

/*
思路二:优化递归。双变量index和c构成了多个数据对,递归过程中存在对一个数据对的多次重复计算。
       即存在重叠子问题和最优子结构,使用记忆化搜索进行优化。
*/
class Knapsack01_2{

private:
    //memo[i][j]表示给定一个index和c之后相应的状态,用于进行记忆化搜索
    vector<vector<int>> memo;

    //考虑0-index的物品,填充当前背包容量c,组合的最大值
    int bestValue(const vector<int> &w,const vector<int> v,int index,int c){

        if(index<0||c<=0){ //递归出口
            return 0;
        }

        /*
        与单纯递归的区别:先检查memo中是否存储了计算结果(避免重复计算)
        */
       if(memo[index][c]!=-1){
           return memo[index][c];
       }

        int res=bestValue(w,v,index-1,c);
        if(c>=w[index]){
            res=max(res,v[index]+bestValue(w,v,index-1,c-w[index]));
        }
        //完成记忆化的过程,将当前给定index、c的状态结果记录到memo[][]中
        memo[index][c]=res;
        return res;

    }

public:
    int knapsack01(const vector<int> &w,const vector<int> &v,int C){
        int n=w.size();
        /*
        为了实现记忆化搜索,基于两个约束条件的情况,构建一个二维数组。共有n行(对应n个物品)
        有C+1列,对应0-C个背包容量,且初始化为-1。
        */
        memo=vector<vector<int>>(n,vector<int>(C+1,-1)); 
        return bestValue(w,v,n-1,C);//递归函数bestValue,考虑0-n-1共n个物品

    }
}; 


/*
思路三:自底向上分析,实现动态规划。
*/
class Knapsack01_3{

private:
    //memo[i][j]表示给定一个index和c之后相应的状态,用于进行记忆化搜索
    vector<vector<int>> memo;

    //考虑0-index的物品,填充当前背包容量c,组合的最大值
    int bestValue(const vector<int> &w,const vector<int> v,int index,int c){

        if(index<0||c<=0){ //递归出口
            return 0;
        }

        /*
        与单纯递归的区别:先检查memo中是否存储了计算结果(避免重复计算)
        */
       if(memo[index][c]!=-1){
           return memo[index][c];
       }

        int res=bestValue(w,v,index-1,c);
        if(c>=w[index]){
            res=max(res,v[index]+bestValue(w,v,index-1,c-w[index]));
        }
        //完成记忆化的过程,将当前给定index、c的状态结果记录到memo[][]中
        memo[index][c]=res;
        return res;

    }

public:
    int knapsack01(const vector<int> &w,const vector<int> &v,int C){
        int n=w.size();
        if(n==0){
            return 0;
        }
        /*
        为了实现记忆化搜索,基于两个约束条件的情况,构建一个二维数组。共有n行(对应n个物品)
        有C+1列,对应0-C个背包容量,且初始化为-1。
        */
        memo=vector<vector<int>>(n,vector<int>(C+1,-1)); 
        //return bestValue(w,v,n-1,C);//递归函数bestValue,考虑0-n-1共n个物品
        //动态规划不同于自顶向下的递归,它是自底向上思考,故先处理最基础的问题
         
        //考虑memo第0行
        for(int j=0;j<C+1;j++){
            memo[0][j]=(j>=w[0]?v[0]:0);
        }
        //解决最基础的问题后,剩下是向上递推的过程,即逐行逐列的更新值
        for(int i=1;i<n;i++){
            for(int j=0;j<C+1;j++){

                memo[i][j]=memo[i-1][j];
                if(j>=w[i]){
                    memo[i][j]=max(memo[i][j],v[i]+memo[i-1][j-w[i]]);
                }
            }
        }
        return memo[n-1][C];


    }
}; 

 int main(){
     return 0;
 }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值