当前位置:   article > 正文

代码随想录算法训练营day41_动态规划

代码随想录算法训练营day41_动态规划

46_携带研究材料

题目:

题目描述

小明是一位科学家,他需要参加一场重要的国际科学大会,以展示自己的最新研究成果。他需要带一些研究材料,但是他的行李箱空间有限。这些研究材料包括实验设备、文献资料和实验样本等等,它们各自占据不同的空间,并且具有不同的价值。 

小明的行李空间为 N,问小明应该如何抉择,才能携带最大价值的研究材料,每种研究材料只能选择一次,并且只有选与不选两种选择,不能进行切割。

输入描述

第一行包含两个正整数,第一个整数 M 代表研究材料的种类,第二个正整数 N,代表小明的行李空间。

第二行包含 M 个正整数,代表每种研究材料的所占空间。 

第三行包含 M 个正整数,代表每种研究材料的价值。

输出描述

输出一个整数,代表小明能够携带的研究材料的最大价值。 

输入示例
  1. 6 1
  2. 2 2 3 1 5 2
  3. 2 3 1 5 4 3
输出示例
5
提示信息

小明能够携带 6 种研究材料,但是行李空间只有 1,而占用空间为 1 的研究材料价值为 5,所以最终答案输出 5。 

数据范围: 
1 <= N <= 5000 
1 <= M <= 5000 
研究材料占用空间和价值都小于等于 1000

算法思想:

动态规划

1、dp[i][j]动态数组的含义:最大重量为j的背包,从0~i号物品中选择装入,可装入的最大物品价值

2、初始化 

dp[i][0] = 0;    // 背包最大容量为0时,可装入价值为0

当只允许选物品0放入背包中时即i=0时,如果j>=weight[0],dp[0][j] = values[0];否则,dp[0][j] = 0;

3、递推公式

当前物品重量大于背包最大容量,则不装,dp[i][j] = dp[i-1][j];

当前物品重量小于背包最大容量,考虑装还是不装 

dp[i][j] = max(dp[i-1][j],dp[i-1][j-weight[i]]+values[i]);

        不装,dp[i][j] = dp[i-1][j];

        装,dp[i][j] = dp[i-1][j-weight[i]]+values[i]

4、遍历顺序,从上到下,从左到右

代码:

  1. //
  2. // main.cpp
  3. // kama_46_携带研究材料
  4. //
  5. // Created by 徐精泽 on 2024/4/23.
  6. //
  7. #include <iostream>
  8. using namespace std;
  9. #include <cmath>
  10. int main() {
  11. int M,N;
  12. cin>>M>>N;
  13. int weight[M],values[M];
  14. for(int i=0;i<M;i++){
  15. cin>>weight[i];
  16. }
  17. for(int i=0;i<M;i++){
  18. cin>>values[i];
  19. }
  20. int dp[M][N+1];
  21. // dp[i][j] 的含义:最大重量为j的背包,从0~i号物品中选择装入,可装入的最大物品价值
  22. //初始化
  23. for(int i=0;i<M;i++){
  24. dp[i][0] = 0; // 背包最大容量为0时,可装入价值为0
  25. // cout<<"dp["<<i<<"][0]="<<dp[i][0]<<" "<<endl;
  26. }
  27. for(int j=0;j<=N;j++){ // 当只允许选物品0放入背包中时
  28. if(j>=weight[0])
  29. dp[0][j] = values[0];
  30. else
  31. dp[0][j] = 0;
  32. // cout<<"dp[0]["<<j<<"]="<<dp[0][j]<<endl;
  33. }
  34. // 递推公式
  35. // 不放入时,dp[i][j] = dp[i-1][j];
  36. // 放入时,dp[i][j] = dp[i-1][j-wight[i]]+values[i];
  37. // 遍历,遍历顺序,从左到右,从上到下
  38. for(int i=1;i<M;i++){
  39. for(int j=1;j<=N;j++){
  40. if(j<weight[i]) dp[i][j] = dp[i-1][j];
  41. else dp[i][j] = max(dp[i-1][j],dp[i-1][j-weight[i]]+values[i]);
  42. // cout<<dp[i][j]<<" ";
  43. }
  44. // cout<<endl;
  45. }
  46. cout<<dp[M-1][N];
  47. return 0;
  48. }

46_携带研究材料_一维数组写法

题目:

同上

算法思想:

基本上与二维数组类似,只是两层for循环中,直接在上一层第二维数组的基础上进行覆盖更新。因为每次更新要用到前面的值,因此应该从后往前遍历,防止从前往后遍历,处理后面的元素时前面的元素值已经被覆盖更新。

代码:

  1. //
  2. // main.cpp
  3. // kamal_46_携带研究材料
  4. //
  5. // Created by 徐精泽 on 2024/4/24.
  6. //
  7. #include <iostream>
  8. using namespace std;
  9. #include <cmath>
  10. int main() {
  11. // 一维数组
  12. // dp[j]含义:表示背包容量最大为j时,可存入物品的最大价值
  13. // 递推公式:dp[j] = max(dp[j],dp[j-wight[i]]+values[i])
  14. // 跟二维数组一样是两层for循环,唯一不同的是上一层的状态隐藏掉了,直接在上一层状态的基础上进行更新。
  15. // 因为求dp[j]要用到上一层的dp[j]和dp[j-wight[i]]
  16. // 为了防止上层数据已被修改覆盖导致出错,应该cognitive后向前遍历
  17. int M,N; // M表示物品数、N表示背包最大容量
  18. cin>>M>>N;
  19. int weight[M],values[M];
  20. for(int i=0;i<M;i++)
  21. cin>>weight[i];
  22. for(int i=0;i<M;i++)
  23. cin>>values[i];
  24. // 定义dp数组
  25. int dp[N+1];
  26. // 初始化dp数组
  27. for(int i=0;i<=N;i++){
  28. if(i>=weight[0])
  29. dp[i]=values[0];
  30. else
  31. dp[i]=0;
  32. }
  33. //两层for循环更新dp数组
  34. for(int i=1;i<M;i++){
  35. for(int j=N;j>=0;j--){ // 从后往前遍历
  36. if(j>=weight[i]){
  37. dp[j]=max(dp[j],dp[j-weight[i]]+values[i]);
  38. }
  39. }
  40. }
  41. cout<<dp[N];
  42. return 0;
  43. }

416_分割等和子集

题目:

给你一个 只包含正整数 的 非空 数组 nums 。请你判断是否可以将这个数组分割成两个子集,使得两个子集的元素和相等。

示例 1:

输入:nums = [1,5,11,5]
输出:true
解释:数组可以分割成 [1, 5, 5] 和 [11] 。

示例 2:

输入:nums = [1,2,3,5]
输出:false
解释:数组不能分割成两个元素和相等的子集。

算法思想:

这题就是上面两个背包问题的实际应用。关键把题意转化成,从物品集中挑选物品,能否正好凑成价值为sum/2。每个物品的重量为nums[i] 价值为nums[i] ,背包最大容量为sum/2

写了用一维数组和二维数组两种写法

代码:

二维数组

  1. class Solution {
  2. // 二维数组
  3. public static boolean canPartition(int[] nums) {
  4. // 算法思想:转化成动态规划,数组元素和为sum,实际上就是问能不能从集合中找到元素正好凑成sum/2
  5. // 题目变为:从重量为nums[i]价值为nums[i]的物品中,恰好装入容量为sum/2大小的背包中
  6. int sum = 0;
  7. for(int i=0;i<nums.length;i++){
  8. sum += nums[i];
  9. }
  10. float target = (float)sum/2;
  11. // 定义二维动态数组dp[i][j]含义:从0~i物品中选择加入最大容量为j的背包的最大价值
  12. int dp[][] = new int[nums.length][(int)target+1];
  13. // 初始化动态数组
  14. // 当背包最大容量为0时,价值为0
  15. for (int i = 0; i < nums.length; i++) {
  16. dp[i][0] = 0;
  17. }
  18. // 当只装入0号物品时
  19. for (int i = 0; i <= (int)target; i++) {
  20. dp[0][i] = 0;
  21. }
  22. // 两层for循环,递推
  23. for (int i = 1; i < nums.length; i++) {
  24. for (int j = 0; j <= (int)target; j++) {
  25. if(j<nums[i])
  26. dp[i][j] = dp[i-1][j];
  27. else
  28. dp[i][j] = Math.max(dp[i-1][j],dp[i-1][j-nums[i]]+nums[i]);
  29. if(j==target&&dp[i][j]==target)
  30. return true;
  31. }
  32. }
  33. return false;
  34. }
  35. }

一维数组

  1. class Solution {
  2. public static boolean canPartition(int[] nums) {
  3. // 算法思想:转化成动态规划,数组元素和为sum,实际上就是问能不能从集合中找到元素正好凑成sum/2
  4. // 题目变为:从重量为nums[i]价值为nums[i]的物品中,恰好装入容量为sum/2大小的背包中
  5. int sum = 0;
  6. for (int i = 0; i < nums.length; i++) {
  7. sum += nums[i];
  8. }
  9. float target = (float) sum / 2;
  10. // 定义动态数组dp
  11. int dp[] = new int[(int) target + 1];
  12. // 初始化动态数组
  13. for (int i = 0; i <= target; i++)
  14. dp[i] = 0;
  15. // 两层for循环遍历
  16. for (int i = 0; i < nums.length; i++) { // 外层物品数
  17. for (int j = (int) target; j >= 0; j--) { // 内层背包大小,从后向前遍历
  18. if (j >= nums[i]) {
  19. dp[j] = Math.max(dp[j], dp[j - nums[i]] + nums[i]); // 递推公式
  20. if ((float) dp[j] == target)
  21. return true;
  22. }
  23. }
  24. }
  25. return false;
  26. }
  27. }

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家小花儿/article/detail/660311
推荐阅读
相关标签
  

闽ICP备14008679号