算法学习 | day36/60 背包问题/分割等和子集

一、题目打卡

        1.1 二维背包问题

        题目链接:46. 携带研究材料(第六期模拟笔试)

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


int n, bagweight;// bagweight代表行李箱空间
void solve() {
    vector<int> weight(n, 0); // 存储每件物品所占空间
    vector<int> value(n, 0);  // 存储每件物品价值
    for(int i = 0; i < n; ++i) {
        cin >> weight[i];
    }

    for(int j = 0; j < n; ++j) {
        cin >> value[j];
    }
    // 一开始就初始化为0,第一列就不用考虑了
    vector<vector<int>> dp(n,vector<int>(bagweight + 1,0));
    
    // 第一行的初始化要从满足重量要求的位置开始初始化
    for(int i = weight[0]; i <= bagweight;i++){
        dp[0][i] = value[0];
    }
    
    for(int i = 1; i < n;i++){
        for(int j = 1 ; j <= bagweight;j++){
            if(weight[i] > j){
                dp[i][j] = dp[i-1][j]; // 超重了就继承上方
                continue;
            }
            // 不选择i
            dp[i][j] = max(dp[i][j],dp[i-1][j]); // 来自上方
            //选择i
            dp[i][j] = max(dp[i][j],dp[i-1][j - weight[i]] + value[i]);
        }
    }
    // for(auto &i:dp){
    //     for(auto &j:i){
    //         cout << j << " ";
    //     }
    //     cout << endl;
    // }
    
    cout << dp[n - 1][bagweight];
}

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

       

         先看了视频再做的,思路相对清楚了很多,我感觉这个题目递归的过程不是很复杂,反而不好理解的是这个状态的定义,还有就是写的过程中,这个索引用着的感觉也有点奇怪,但是我说不清楚,还是写一个案例自己分析一下比较好,然后就是注意一下,本身在递推的过程中,我忽略的一个情况,就是当前的物品重量肯定放不进的情况,还有就是dp[i-1][j-weight],这个地方的j,我写成的bagWeight,这样就没有递推的过程了。

        然后是滚动数组的写法,这样确实和二维数组有很多不一样的地方:

// 滚动数组
void solve_with_scrolling_array(){
    // 这里 dp 数组的定义就改变了,这里的dp[i]代表的是背包容量为i的时候,用所有的资源能装下的最大的价值
    vector<int> dp(bagweight+1,0), weight(n,0), value(n,0);
    for(int i = 0; i < n;i++){
        cin >> weight[i];
    }
    
    for(int i = 0; i < n;i++){
        cin >> value[i];
    }
    // for(int i = weight[0];i<bagweight;i++){
    //     dp[i] = value[0];
    // }
    
    for(int i = 0 ; i < n;i++){ // 这里的循环代表的是选择的种类索引
        for(int j = bagweight; j >= weight[i];j--){
            // if(weight[i] > j) continue;
            dp[j] = max(dp[j],dp[j-weight[i]] + value[i]); //dp[j]其实就表示的是这个值不发生变化,也就是不选择
        }
    }
    cout << dp[bagweight];
}

        首先是这个不能用第一个物品进行初始化,因为这样就是默认第一个物品加进去了,这样用后面倒序的方法的时候,会计算重复,之所以要用倒序,是因为一维数组无法存储左上和上的数据,这样使用当前层,就会导致当前的物品被计算了多次,所以要从后向前进行计算。特别的是这个终止的条件,其实就是之前二维数组 if 中需要继承上一个数值的一个处理。

        1.2 分割等和子集

        题目链接:. - 力扣(LeetCode)

class Solution {
public:
    bool canPartition(vector<int>& nums) {
        int sum = 0;
        for(auto& it: nums){
            sum += it;
        }
        if(sum % 2 != 0) return false; // 这种不可能分割成

        int n = nums.size(), bagweight = sum/2;
        vector<int> dp(bagweight + 1,0);

        for(int i = 0; i < n;i++){ // 相当于遍历每个物品
            for(int j = bagweight; j >= nums[i];j--){
                dp[j] = max(dp[j],dp[j - nums[i]] + nums[i]);
            }
        }
        if(dp[bagweight] == sum/2) return true;
        return false;


        // sort(nums.begin(),nums.end());
        // int tmp = 0;
        // int i = 0;
        // for(; i < nums.size();i++){
        //     tmp += nums[i];
        //     if(tmp == sum/2) break;
        // }
        // ++i;
        // int tmp1 = 0;
        // for(;i<nums.size();i++){
        //     tmp1 += nums[i];
        // }
        // return tmp1 == tmp;

    }
};

        做的磕磕绊绊的,主要是这个题的价值和重量是同一个东西,让我一时没反映过来。

  • 5
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
0/1背包问题是指:有一个容量为C的背包和n个物品,每个物品有一个重量w和一个价值v,要求在不超过背包容量的情况下,选出一些物品放入背包,使得背包中物品的总价值最大。 分支限界法是一种求解最优化问题的重要方法,它可以用于求解0/1背包问题。具体步骤如下: 1. 定义节点:将可行解空间按照某种方式分解为多个子集,每个子集对应一个节点,节点代表了一个可行解的一部分。 2. 定义限界函数:对于每个节点,定义一个限界函数,用于估计该节点的可行解空间中可能存在的最优解的上限,以便在搜索的过程中优先考虑可能存在的最优解。 3. 搜索过程:从根节点开始,按照某种方式对节点进行扩展和剪枝,直到找到最优解或无法继续扩展为止。搜索过程中需要维护一个活结点表,用于保存待扩展的节点信息。 具体到0/1背包问题,分支限界法的搜索过程如下: 1. 初始化:将根节点加入活结点表中,背包的剩余容量为C,当前节点代表的可行解包含所有物品。 2. 搜索过程: 1)从活结点表中取出一个节点。 2)如果该节点代表的可行解已经是完整的,则更新最优解,并进行剪枝操作。 3)否则,对该节点进行扩展,分别生成两个子节点:一个子节点表示选择当前物品放入背包,另一个子节点表示不选择当前物品放入背包。对于两个子节点分别计算限界函数,将它们加入活结点表中。 4)重复以上步骤,直到活结点表为空或找到最优解。 3. 输出最优解。 在具体实现时,可以使用优先队列来实现活结点表,以便在搜索过程中优先处理限界函数最小的节点。同时,可以使用动态规划的思想来计算限界函数,以减少重复计算。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值