代码随想录算法训练营Day35||01背包

一、01背包

        (1)dp数组定义:当目前背包容量为j时,从第0件物品遍历到第i件物品的最大价值

        (2)递推公式:dp[i][j]=dp[i-1][j](一定装不下第i件物品,即j<weight[i]时)

                                   dp[i][j]=dp[i-1][j](能装下,但是不装这件时)

                                               value[i]+dp[i-1][j-weight[i]](能装下,同时也要装下这件)

                因为本题的遍历j是从0开始遍历,临界点是j为第i件物品的质量,所以能装下的情况可能是要把之前装的所有的物品都拿出去,再放第i件,因此第i件单件的价值可能不如之前小件加起来都和。所以在能装下的情况里边又包含了两种情况进行比较,取最大值的操作。

        (3)初始化:在建立数组的时候,默认全为0,因此第一列不用管,其含义是当背包容量为0时,所有的物品都装不下,因此价值也都为0。第一行要从j=weight[0]时开始,第一行第j列以后全都赋值成value[0],其含义是在等于weight[0]之前,背包放不下,默认价值为0,等到可以放下以后,(同时第一行的意思是只放第0件物品)后面全都赋值为第0件物品的价值。

        (4)遍历顺序:在物品内遍历容量

        (5)返回右下角的元素

#include<iostream>
#include<vector>
using namespace std;


int m,bagweight;


void solve(){
    vector<int>weight(m,0);
    vector<int>value(m,0);
    for(int i=0;i<weight.size();i++){
        cin>>weight[i];
    }
    for(int j=0;j<value.size();j++){
        cin>>value[j];
    }
    
    vector<vector<int>>dp(m,vector<int>(bagweight+1,0));
    //初始化
    for(int i=weight[0];i<=bagweight;i++){
        dp[0][i]=value[0];
        
    }
    
    for(int i=1;i<m;i++){
        for(int j=0;j<=bagweight;j++){
            if(j<weight[i])dp[i][j]=dp[i-1][j];
            else{
                dp[i][j]=max(dp[i-1][j],value[i]+dp[i-1][j-weight[i]]);
            }
        }
    }
    cout<<dp[m-1][bagweight];
  
}




int main(){
   while(cin>>m>>bagweight){
       solve();
   }
   return 0;
}

注意这里的m是物品的种类数,而i是第i件,前者从1开始计数,后者从0开始计数,这里的开闭要注意。

二、回滚数组,一维数组实现01背包

        和二维数组的区别是:

                这里需要倒序遍历背包容量,以此来保证每件物品只取一次。

        

#include<iostream>
#include<vector>
#include<math.h>
using namespace std;
int main(){
    int m,bagspace;
    cin>>m>>bagspace;
    vector<int>weight(m+1,0);
    vector<int>value(m+1,0);
      
    vector<int>dp(bagspace+1,0);
      
      
       for(int i=1;i<weight.size();i++){
           cin>>weight[i];
       }
       for(int j=1;j<value.size();j++){
           cin>>value[j];
       }
         
       for(int i=1;i<=m;i++){
           for(int j=bagspace;j>0;j--){
               if(j<weight[i])dp[j]=dp[j];
               else dp[j]=max(dp[j],dp[j-weight[i]]+value[i]);
           }
       }
         
       cout<<dp[bagspace];
         
       return 0;
      
}

    三、切割等和子集

        这题很难想到的点是如何把它转化成背包问题,即让价值等于重量 。不过我理解的是,判断在背包可以被装满(j==target)的时候,此时是否被装满 ,不引入价值这个概念。

        需要注意的是,求和之后先判断它是否为奇数,若为奇数则直接返回不成立。  

class Solution {
public:
    bool canPartition(vector<int>& nums) {
        int sum=0;
        for(int i=0;i<nums.size();i++){
            sum+=nums[i];
        }
        if(sum%2!=0)return false;

        int target=sum/2;

        vector<int>dp(target+1,0);

        for(int i=0;i<nums.size();i++){
            for(int j=target;j>=nums[i];j--){
                dp[j]=max(dp[j],dp[j-nums[i]]+nums[i]);
            }
        }
        if(dp[target]==target)return true;

        return false;
    }
};

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值