动态规划之0-1背包问题

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

解题思路
1. 递 规(自顶向下)
#include<vector>
using namespace std;
class Knapsacko1 {
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=bestValue(w,v,index-1,c);//第index个物品的重量超出了对应的容量c;
        if(c>=w[index])
            res=max(res,v[index]+bestValue(w,v,index,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)//考虑将第n个物品添加到背包中
            
    }
};
由于会出现(index,c)数据对而重复的计算,所以使用记忆化搜索对算法进行改进。因为有两个限制条件w[index],v[index],因此使用二维数组对其进行保存vector<vector<int>>memory
2. 递 规+记忆化搜索(自顶向下)
#include<vector>
using namespace std;
class Knapsacko1 {
private:
    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;
        if(memo[index][c]!=-1)
            return memo[index][c];
        int res=bestValue(w,v,index-1,c);//第index个物品的重量超出了对应的容量c;
        if(c>=w[index])
            res=max(res,v[index]+bestValue(w,v,index,c-w[index]));
        memo[index][c]=res;
        return res;
    }
public:
    int knapsack01(const vector<int>& w,const vector<int>& v,int C) {
        int n=w.size(); 
        memo=vector<vector<int>(n,vector<int>(C+1,-1));
        return bestValue(w,v,n-1,C)//考虑将第n个物品添加到背包中
            
            
    }
};
3. 动态规化(自底向上)关于二维数组

在这里插入图片描述

右下角的方格便是从编号为[0,1,2]三个物品中选择,使得容量不超过5的情况下获得最大的价值
在这里插入图片描述

在这里插入图片描述

#include<vector>
using namespace std;
class Knapsacko1 {
private:

public:
    int knapsack01(const vector<int>& w,const vector<int>& v,int C) {
        assert()
        int n=w.size(); 
        if(n=0)
            return 0;
        memo=vector<vector<int>(n,vector<int>(C+1,-1));
        
        for(int j=0;j<=C;j++)
            memo[0][j]=(j>=w[0]?v[0]:0);
        for(int i=1;i<n;i++)
            for(int j=0;j<=C;j++)
            {   memo[i][j]=memo[i-1][j];//第一个策略,不放w[i]
                if(j>=w[j])
                    memo[i][j]=max(memo[i,j],v[i]+memo[i-1][j-w[j]]);/
                    //将第i个物器发入背包
            }
        return memo[n-1][C];
            
    }
};
0-1背包问题的优化1

以上代码的空间和时间复杂度均为O(n*C),其空间复杂度可以进一步优化
在这里插入图片描述
在这里插入图片描述
通过两行轮流,便可以实现,代码如下

#include<vector>
using namespace std;
class Knapsacko1 {
private:

public:
    int knapsack01(const vector<int>& w,const vector<int>& v,int C) {
        assert()
        int n=w.size(); 
        if(n=0)
            return 0;
        memo=vector<vector<int>(2,vector<int>(C+1,-1));
        
        for(int j=0;j<=C;j++)
            memo[0][j]=(j>=w[0]?v[0]:0);
        for(int i=1;i<n;i++)
            for(int j=0;j<=C;j++)
            {   memo[i%2][j]=memo[(i-1)%2][j];//第一个策略,不放w[i]
                if(j>=w[j])
                    memo[i%2][j]=max(memo[i%2,j],v[i]+memo[(i-1)%2][j-w[j]]);/
                    //将第i个物器发入背包
            }
        return memo[(n-1)%2][C];
            
    }
};
0-1背包问题的空间优化2

那么我们能否使用一行大小为C的一维数组实现动规化呢?
在这里插入图片描述
实际上我们在考虑第i 个物品能否放入容量为c 的容器中时,只与上一行左边的元素有关,而于右边的无关。因此如果是使用一维数组更新的话,为了保证右边的格子的正确性,我们应该从右往左刷新格子。

在这里插入图片描述
在这里插入图片描述
对于1号与0号容量小于2 号,无法盛下,故可以提前结束程序。

#include<vector>
using namespace std;
class Knapsacko1 {
private:

public:
    int knapsack01(const vector<int>& w,const vector<int>& v,int C) {
        assert()
        int n=w.size(); 
        if(n=0)
            return 0;
        vector<int>memo(C+1,-1);
        
        for(int j=0;j<=C;j++)
            memo[j]=(j>=w[0]?v[0]:0);
        for(int i=1;i<n;i++)
            for(int j=C;j>=w[j];j--)
            {  
                memo[j]=max(memo[j],v[i]+memo[j-w[j]]);
                    //将第i个物器发入背包
            }
        return memo[C];
            
    }
};
0-1背包问题的变种
  1. 完全背包问题:每个物品可以无限使用转化为有限个物品的使用,只不过物品序列中有重复的,此外可以进一步优化
  2. 多重背包问题:每个物品不止一个有nums[i]
  3. 多维费用背包问题:要考虑科技二路品的体积与重量。
  4. 物品间相互约束关系:依赖问题、排拆问题
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值