动态规划之背包问题

一:0-1背包问题

  •  容量V是体积或者重量的定义
  •  物品总数:n,每个物品有自己的体积或者重量+自己的价值
  •  求:在不超过容量的前提下,使得总价值最大

首先求问题的状态,也就是解决问题的函数

 这里应该有2个参数:物品总数和背包容量

F(n,C)----把n个物品有选择地放入容量为C的背包,使得F(放入背包的物品总价值)最大

说白了有两种状态:

  • 第一种:F(i,c)=F(i-1,c)-------当前这i个物品,不把这个物品放入背包
  • 第二种:F(i,c)=v(i)+F(i-1,c-w(i))------选择了这个物品,然后背包容量减去该体积

上述就是:

自顶向下解决问题,又叫状态转移方程

一:自顶向下解决问题 

#include<vector>
#include<algorithm>
using namespace std;
class soluction{
private:
   vector<int> w;
   vector<int> v;
   //记忆化搜索:memo[index][c]---当前的index物品的总容量为c
   vector<vector<int>> memo;
   int bestValue(int index,int c){
    //从0~index的物品中选,当前背包容量c
    if(index<0||c<=0) return 0;
    if(memo[index][c]!=-1) return memo[index][c];
    int res=bestValue(index-1,c);
    if(w[index]<=c){//当前物品放入包中
        res=max(res,v[index]+bestValue(index-1,c-w[index]));
    }
    memo[index][c]=res;
    return res;
   }
public:
   int knapsack(const vector<int>& w,const vector<int>& v,int capcity){
    //参数:n个物品的重量和价值,背包的容量
    this->w=w;
    this->v=v;
    int n=w.size();
    memo=vector<vector<int>>(n,vector<int>(capcity+1,-1));
    return bestValue(n-1,capcity);
}
};

memo=vector<vector<int>>(n,vector<int>(capcity+1,-1));

-----注意是可以取到容量capcity,所以vector容量是+1


不难,思考方式就是选不选该物品,只不过采取倒着的方式,限制就是容量

注意在选择该物品时候,要保证加入该物品,容量不超标。

二:自底向上解决问题 

 

 

 

 

class soluction{
public:
   int knapsack(const vector<int>& w,const vector<int>& v,int cap){
       assert(w.size()==v.size());
       int n=w.size();
       vector<vector <int>> memo(n,vector <int>(cap+1,-1));
       //自底向上
       for(int j=0;j<=cap;j++){
           memo[0][j]=(j>=w[0]?v[0]:0);
       }
       for(int i=1;i<n;i++){
        for(int j=0;j<=cap;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][cap];
   }
};

首先计算第一行---也就是只考虑编号为0的物品(保证背包容量大于该物品)

接下来依据第一行,计算1~n-1行

依据容量0~cap计算------要么取,要么不取。取后把已经计算的加入


 

进行优化 

int knapsack_2(const vector<int>& w,const vector<int>& v,int c){
    /*优化一:因为只有memo[n-1][c]是我们的想要的结果
             当前行的数值只依赖与前一行,所以考虑只开辟两行
             利用奇偶去覆盖行数*/
        int n=w.size();
        vector<vector<int>> memo(2,vector<int>(c+1,-1));
        for(int j=0;j<=c;j++)
            memo[0][j]=(w[0]<=j?w[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];//先默认取前一行
                if(w[i]<=j){
                     memo[i%2][j]=//二选一
                         max(memo[i%2][j],v[i]+memo[(i-1)%2][j-w[i]]);
                }
            }
        return memo[(n-1)%2][c];//注意是n-1;
    }
int knapsack_2(const vector<int>& w,const vector<int>& v,int c){
    /*优化一:因为只有memo[n-1][c]是我们的想要的结果
             当前行的数值只依赖与前一行,所以考虑只开辟两行
             利用奇偶去覆盖行数*/
        int n=w.size();
        vector<vector<int>> memo(2,vector<int>(c+1,-1));
        for(int j=0;j<=c;j++)
            memo[0][j]=(w[0]<=j?w[0]:0);
        for(int i=1;i<n;i++)
            for(int j=0;j<=c;j++){
                memo[i&1][j]=memo[(i-1)%2][j];//先默认取前一行
                if(w[i]<=j){
                     memo[i&1][j]=//二选一
                         max(memo[i&1][j],v[i]+memo[(i-1)&1][j-w[i]]);
                }
            }
        return memo[(n-1)&1][c];//注意是n-1;
    }

奇偶可以利用:%2或&1来计算

 优化二思想:

因为更新时候,如果后一行的容量比新考虑的物品编号值小,那么直接复制前一行的结果,所以选择从后往前考虑,这样可以直接在原基础上进行覆盖,不必将物品编号单设一个维度

int knapsack_single(const vector<int>& w,const vector<int>& v,int c){
        int n=w.size();
        vector<int>memo(c+1,-1);//只记录容量
        for(int j=0;j<=c;j++)
            memo[j]=(w[0]<=j?v[0]:0);
        for(int i=1;i<n;i++)
           for(int j=c;j>=w[i];j--)//从后往前编号
            if(w[i]<=j){
                memo[j]=max(memo[j],v[i]+memo[j-w[i]]);
            }
        return memo[c];
    }

这里因为只有一行,所以只从改变的开始,也就是j容量在大于新编号容量时候才更新

而且由于有了数据,不去考虑不变情况

最后返回memo[C]

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值