数据结构笔记--背包问题

1--0-1背包问题

0-1 背包问题的特征:

        一共有 n 个物品,但每个物品只能选择一次;

二维 dp 解法:

        dp[i][j] 表示背包容量为 j ,可以在 0-i 种物品选取,其最大价值;

        初始化:dp[0][j] = value[0](j >= weight[0]),dp[i][0] = 0;

        状态转移方程:dp[i][j] == std::max(dp[i-1][j], dp[i-1][j-weight[i]] + value[i]),其中 dp[i-1][j] 表示不选择物品 i,dp[i-1][j-weight[i]] + value[i] 表示选择物品 i;

        遍历顺序:两个 for 循环来正序遍历物品 i 和背包容量 j(可以先遍历物品 i,再遍历背包容量 j;也可以先遍历背包容量 j,再遍历物品 i);

一维 dp 解法:

        dp[j] 表示背包容量为 j 时,其最大价值;

        初始化:dp[0] = 0;

        状态转移方程:dp[j] =  std::max(dp[j], dp[j - weight[i]] + value[i]),其中 dp[j - weight[i]] + value[i] 表示选取物品 i 时;

        遍历顺序:两个 for 循环,第一个 for 循环正序遍历物品,第二个 for 循环倒叙(j 从大到小)遍历背包容量;(不能先遍历背包容量,再遍历物品);

2--分割等和子集

数据结构笔记--背包问题_第1张图片

主要思路:

        可以转换为 0-1 背包问题,将一半的和作为背包的容量,另一半的和作为物品的价值,同时每个物品的价值和重量相同;

        计算背包的容量:target = sum / 2;

        初始化:dp[0] = 0;

        状态转移方程:dp[j] = max(dp[j], dp[j - nums[i]] + nums[i]);j 表示背包的容量,dp[j]表示背包容量为 j 时,背包所装物品的最大价值;

        遍历顺序:经典0-1背包一维遍历顺序,先正序遍历物品,再倒叙遍历背包容量;

        当 dp[target] == target 时,表明恰好可以将数组划分为两个子集,一个子集的和作为背包容量,另一个子集的和作为物品的价值;

#include 
#include 

class Solution {
public:
    bool canPartition(std::vector& nums) {
		int sum = 0;
		for(int num : nums){
			sum += num;
		}
		if(sum % 2 == 1) return false; // 不能二等分
		int target = sum / 2; // 一半和当作容量,另一半和作为价值
		std::vector dp(target+1, 0);
		
		// 初始化
		dp[0] = 0;
		// 遍历
		for(int i = 0; i < nums.size(); i++){
			for(int j = target; j >= nums[i]; j--){ // j >= nums[i] 确保容量大于重量,这里物品的重量和价值相同,均为nums[i]
				dp[j] = std::max(dp[j], dp[j - nums[i]] + nums[i]);
			}
		}
		if(dp[target] == target) return true; // 一半作为容量,另一半作为价值,因此可以等分
		return false;
    }
};

int main(int argc, char *argv[]) {
	// nums = [1,5,11,5]
	std::vector test = {1, 5, 11, 5};
	Solution S1;
	bool res = S1.canPartition(test);
	if(res) std::cout << "true" << std::endl;
	else std::cout << "false" << std::endl; 
	return 0;
}

3--最后一块石头的重量II

数据结构笔记--背包问题_第2张图片

主要思路:

        类似于上题,可以转换为 0-1 背包问题;用一半的石头作为背包容量,另一半石头作为物品;

        背包装尽可能多的石头,最后两者相减相消即可得到最小的重量;

#include 
#include 

class Solution {
public:
    int lastStoneWeightII(std::vector& stones) {
		int sum = 0;
		for(int num : stones){
			sum += num;
		}
		int vec = sum / 2; // 一半作为容量
		std::vector dp(vec+1, 0);
		// 初始化
		dp[0] = 0;
		// 遍历
		for(int i = 0; i < stones.size(); i++){
			for(int j = vec; j >= stones[i]; j--){
				dp[j] = std::max(dp[j], dp[j - stones[i]] + stones[i]); // 装尽可能多的石头
			}
		}
		return sum - dp[vec] - dp[vec]; // sum - dp[vec] 表示另一半的石头
    }
};

int main(int argc, char *argv[]) {
	// stones = [2,7,4,1,8,1]
	std::vector test = {2, 7, 4, 1, 8, 1};
	Solution S1;
	int res = S1.lastStoneWeightII(test);
	std::cout << res << std::endl;
	return 0;
}

4--

你可能感兴趣的:(数据结构)