【算法训练记录——Day41】


背包!!

1.理论基础——代码随想录

在这里插入图片描述
主要掌握01背包和完全背包
物品数量: 只有一个 —— 01背包;
无数个 —— 完全背包;
不同物品数量不同 —— 多重背包;
按组打包,每组最多选一个 —— 分组背包;

2.纯01背包_kamacoder46

题目描述:有n件物品和一个最多能背重量为w 的背包。第i件物品的重量是weight[i],得到的价值是value[i] 。
          每件物品只能用一次,求解将哪些物品装入背包里物品价值总和最大

思路:

  1. 暴力方法:所有物品只有两种状态,装或者不装,因此可以用回溯法搜索所有情况,然后选择
  2. 二维数组dp01背包:
    1. dp[i][j]表示容量为 j 的背包从前i件(0=<x<i)物品里随便装所取得的物品最大价值
    2. 当物品价值最大时,第i件物品如果不在背包里,那么dp[i][j] = dp[i-1][j];
      如果在背包里,dp[i][j] = dp[i-1][j-w[i]] + v[i];
      dp[i][j] = max(dp[i-1][j], dp[i-1][j-w[i]] + v[i]);
    3. 初始化: 背包容量为0,价值为0;dp[i][0] = 0;
      从定义可知,i 从 1开始有意义, i > 0;
      补充:当 i= 0 时,因为i>0时,dp[i][j]都与dp[i-1][j]相关,因此需要初始化i= 0;如果 j < w[0],那么不用管,本来就放不进去,但如果 j > w[0],此时的dp[0][j]就需要置为v[0]了,因为第一个能放入背包。
    4. 顺序:i从1 ~ n,j 从 w ~ 0;
      先遍历物品和先遍历物品,都差不多
    5. dp[i][j] = max(dp[i-1][j], dp[i-1][j-w[i]] + v[i]);
	#include <iostream>
	#include <vector>
	using namespace std;
	
	int main() {
	    int M, N;
	    cin >> M >> N;
	    vector<vector<int>> item(M, vector<int>(2, 0));
	    
	    for(int i = 0; i < M; i++) {
	        cin >> item[i][0];
	    }
	    for(int i = 0; i < M; i++) {
	        cin >> item[i][1];
	    }
	    
	    vector<vector<int>> dp(M, vector<int>(N + 1, 0));
	    
	    for (int j = item[0][0]; j <= N; j++) {
	        dp[0][j] = item[0][1];
	    }
	    
	    for(int i = 1; i < M; i++) {
	        for(int j = 0; j <= N; j++) {
	            if(item[i][0] > j) dp[i][j] = dp[i-1][j];
	            else dp[i][j] = max(dp[i-1][j], dp[i-1][j-item[i][0]] + item[i][1]);
	        }
	    }
	    
	    cout << dp[M-1][N];
	    
	    return 0;
	}

方法二:一维数组
dp[i][j] = max(dp[i-1][j], dp[i-1][j-item[i][0]] + item[i][1])
可以用dp[i][j]来保存dp[i-1][j],从而转换成一维数组
dp[j] = max(dp[j], dp[j-w[i]+item[i][1]);

	#include <iostream>
	#include <vector>
	using namespace std;
	
	int main() {
	    int M, N;
	    cin >> M >> N;
	    vector<vector<int>> item(M, vector<int>(2, 0));
	    
	    for(int i = 0; i < M; i++) {
	        cin >> item[i][0];
	    }
	    for(int i = 0; i < M; i++) {
	        cin >> item[i][1];
	    }
	    
	    vector<int> dp(N + 1, 0);
	
	    for(int i = 0; i < M; i++) {
	        for(int j = N; j >= item[i][0]; j--) {
	            dp[j] = max(dp[j], dp[j-item[i][0]] + item[i][1]);
	        }
	    }
	    
	    cout << dp[N];
	    
	    return 0;
	}

3.leetcode_416分割等和子集

在这里插入图片描述
思路:
主要问题在于如何从这个问题联想到动态规划

  1. 集合的元素相等,则target = sum >> 1; 相当于给定背包容量,和物品重量,如果能分割,那么背包的最大容量就是最大价值,我们只需求出最大价值然后进行比较即可
  2. dp[j]表示 容量为j的背包最大价值是dp[j]
  3. dp[j] = max(dp[j], dp[j-nums[i]] + nums[i]);
  4. 一维数组背包,j 从后向前,否则会重复使用元素
    bool canPartition(vector<int>& nums) {
		int sum = 0;
		for(int i : nums)
			sum += i;
			
		if(sum % 2 == 1) return false;
		
		int target = sum >> 1;
		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]);	
			}
		
		return dp[target] == target;
	}
  • 7
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值