算法刷题打卡036 | 动态规划4-背包问题

背包问题理论基础

二刷复习背包问题理论基础,并且在IDE中实现数据输入和算法求解:

#include<iostream>  // header for std
#include<vector>
#include<algorithm>  // header for max

void get_bag_inputs(int* n, std::vector<int>& weight, std::vector<int>& value)
{
	std::cin >> *n;  // 需要先输入物品数量
	// weights
	for (int i = 0; i < *n; i++)
	{
		int temp;
		std::cin >> temp;
		weight.push_back(temp);
	}
	// values
	for (int i = 0; i < *n; i++)
	{
		int temp;
		std::cin >> temp;
		value.push_back(temp);
	}
}


int bag_algorithm(int& bagweight, std::vector<int>& weights, std::vector<int>& values)
{
	int n = weights.size();
	std::vector<std::vector<int>> dp(n, std::vector<int>(bagweight+1));
	// 初始化
	for (int i = 0; i < n; i++)
	{
		dp[i][0] = 0;
	}
	for (int j = weights[0]; j < bagweight+1; j++)
	{
		dp[0][j] = values[0];
	}
	// dp数组遍历,先遍历物品,再遍历背包
	for (int i = 1; i < n; i++)
	{
		for (int j = 0; j < bagweight+1; j++)
		{
			if (weights[i] > j)
				dp[i][j] = dp[i - 1][j];
			else
				dp[i][j] = std::max(dp[i - 1][j], dp[i - 1][j - weights[i]] + values[i]);
		}
	}
	return dp[n - 1][bagweight];
}

int main()
{
	std::vector<int> weights;
	std::vector<int> values;
	int bagweight;
	std::cin >> bagweight;
	int num;
	get_bag_inputs(&num, weights, values);
	int max_value = bag_algorithm(bagweight, weights, values);
	printf("\nmax value is: %d", max_value);
	return 0;
}

 实现算法的同时也熟悉一下C++的输入输出以及代码风格(之前Python用习惯了)。要注意的是在二维数组中,bagweight包含容量0,所以背包容量这一维的数组长度为bagweight + 1。

滚动数组

从递推公式上看,二维dp数组计算过程中只用到了上一行的数据,空间复杂度可以优化,使用一维数组滚动更新。但使用滚动数组时,背包容量的遍历顺序就不能从左往右遍历了,从左往右遍历会使得同一个物品重复放置(而实际上同一个物品只能放一次),因此需要倒序遍历。

#include<iostream>  // header for std
#include<vector>
#include<algorithm>  // header for max

void get_bag_inputs(int* n, std::vector<int>& weight, std::vector<int>& value)
{
	std::cin >> *n;
	// weights
	for (int i = 0; i < *n; i++)
	{
		int temp;
		std::cin >> temp;
		weight.push_back(temp);
	}
	// values
	for (int i = 0; i < *n; i++)
	{
		int temp;
		std::cin >> temp;
		value.push_back(temp);
	}
}


int bag_algorithm2(int& bagweight, std::vector<int>& weights, std::vector<int>& values)
{
	int n = weights.size();
	std::vector<int> dp(bagweight + 1);
	// 初始化
	for (int j = weights[0]; j < bagweight + 1; j++)
	{
		dp[j] = values[0];
	}
	// dp数组遍历,先遍历物品,再遍历背包
	for (int i = 1; i < n; i++)
	{
		for (int j = bagweight; j >= weights[i]; j--)
		{
		    dp[j] = std::max(dp[j], dp[j - weights[i]] + values[i]);
		}
	}
	return dp[bagweight];
}


int main()
{
	std::vector<int> weights;
	std::vector<int> values;
	int bagweight;
	std::cin >> bagweight;
	int num;
	get_bag_inputs(&num, weights, values);
	// int max_value = bag_algorithm1(bagweight, weights, values);
	int max_value = bag_algorithm2(bagweight, weights, values);
	printf("\nmax value is: %d", max_value);
	return 0;
}

 bag_algorithm2函数的实现更简洁,遍历背包容量时可以只遍历到weights[i],即只看能放下第i个物品的容量。和讲解代码的区别是,这里用的初始化和二维dp相同,并且物品从1开始遍历;讲解中一维数组初始化为0,物品从0开始倒序遍历背包容量,得出的结果是一样的。

应用题 LeetCode 416 分割等和子集

题目链接:416. 分割等和子集 - 力扣(Leetcode)

这道题需要找到集合里能够总和为sum / 2 的子集,将sum / 2类比为背包容量,物品就是集合中的每个数字,重量和价值都是数值大小,套用01背包的滚动数组解法如下:

class Solution:
    def canPartition(self, nums: List[int]) -> bool:
        s = sum(nums)
        if s % 2 != 0:
            return False
        target = s // 2
        dp = [0] * (target + 1)
        n = len(nums)
        for i in range(n):
            for j in range(target, nums[i] - 1, -1):
                dp[j] = max(dp[j], dp[j-nums[i]] + nums[i])
        
        return True if dp[-1] == target else False

 如果遍历结束后,背包容量为sum / 2的时候刚好能装满,就是可以获得符合要求的子集,时间和空间复杂度和纯背包问题相同。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值