代码随想录算法训练营第三十三天| 1049. 最后一块石头的重量 II 494. 目标和 474.一和零

1049. 最后一块石头的重量 II

思路:

因为含有两个石头的相撞,所以需要把dp的目标值改成sum/2,

然后取得这个目标值的最大值,然后对sum-2*target

代码:

 1 // 要求:有多个石头,两两撞击,取得剩下的石头的最小值
 2 // ——》一定要碰到最后一个
 3 // 注意:
 4 // 1,x==y: 两个粉碎,x<y: y = y-x
 5 // 2,可能剩下一个或者0个 那么怎么判断是0还是1?
 6 // 
 7 // 背包问题:
 8 // dp[n]:当有N个物品的时候,当前重量的最小值
 9 // 
10 // 
11 //  怎么设置? 已经直到重量是这些,但是终止条件是什么?
12 // 
13 // 物品:石头  价值:大小,重量 个数
14 // 容量:物品的数量
15 // 
16 // dp[n]: 撞的话需要取两个石头,但是当前遍历的只有一个石头
17 // 1,不撞 dp[n]
18 // 2,撞: dp[n] = dp[n+1] - (2x)[第一个是遍历y,使得x属于<=y]
19 //
20 
21 //
22 // 如果涉及两个东西之间的比较,可以往容量为sum/2那里思考
23 // dp[n]: 当容量为N  时候的最大值
24 //
25 int lastStoneWeightII(vector<int>& stones) {
26     if (stones.size() == 1) return stones[0];
27 
28     int sum_ = 0;
29     for (int num : stones)
30     {
31         sum_ += num;
32     }
33 
34     vector<int> dp((sum_ / 2)+1, 0);
35 
36     for (int i = 0; i < stones.size(); i++)
37     {
38         for (int j = (sum_ / 2); j >= stones[i]; j--)
39         {
40             dp[j] = max(dp[j], dp[j - stones[i]] + stones[i]);
41         }
42     }
43 
44     int result = sum_ - 2 * dp[sum_ / 2];
45     if (result < 0) return 0;
46     else return result;
47 }

  494. 目标和 

要求:

给你一个整数数组 nums 和一个整数 target 。

向数组中的每个整数前添加 '+' 或 '-' ,然后串联起所有整数,可以构造一个 表达式 :

例如,nums = [2, 1] ,可以在 2 之前添加 '+' ,在 1 之前添加 '-' ,然后串联起来得到表达式 "+2-1" 。
返回可以通过上述方法构造的、运算结果等于 target 的不同 表达式 的数目。

 

思路:

因为是正负,所以只要满足了正的数,那么也就可以满足所有的和了

因此 l + r = sum l-r = target -> l = (sum+target)/2

如果 l 不可以取整,那么就是无解

dp[n] 定义:当和为N时,有多少种求和方法

递推公式

dp[n] = dp[0] + dp[1] +...... dp[n] 

=> dp[n]+=dp[j-nums[i]]  含义:dp[n]+ 当我取nums[i]时,可以有dp[j-nums[i]]种方法凑成J

遍历顺序

先遍历物品,再倒序遍历容量

代码

 1 // 对NUMS 数组前面加 +-,从而让这个值可以等于target,返回有多少种这样的表达式
 2 // 
 3 // 问题:
 4 // 1,统计的是装满的方法
 5 // 2,每个nums都要被用到,而不是可以选择
 6 // 
 7 // 背包问题:在背包的容量情况下,最大装满,同时它的价值最大 ——》让这些数字尽量装满target —》 要知道的是装满的有几种方法
 8 // 
 9 // 让每个值都可以 + -,然后在每次更改状态的时候,记录一下看看他们能否装满?
10 // 
11 // 新思路:
12 // 正负改成两个阵营, l = (sum+target)/2 如果不能整除,那么就返回0
13 // dp[n]: 有几种方法 正数的值 满足N  ——————》》 这个是个难题
14 //    dp[n] = dp[0~n-1]
15 //
16 int findTargetSumWays(vector<int>& nums, int target) {
17     
18     int sum_ = accumulate(nums.begin(), nums.end(), 0);
19     if (sum_ < abs(target)) return 0;
20     if ((sum_ + target) % 2 != 0) return 0;
21 
22     int bgSize = (sum_ + target) / 2;
23     vector<int>dp(bgSize + 1, 0);
24     dp[0] = 1;
25 
26     //dp[n] 和满足N的时候有几种组合方式
27     for (int i = 0; i < nums.size(); i++)
28     {
29         for (int j = bgSize; j >= nums[i]; j--)
30         {
31             dp[j] = dp[j] + dp[j - nums[i]];
32         }
33     }
34 
35     return dp[bgSize];
36 }

  474.一和零  

要求:

输入:strs = ["10", "0001", "111001", "1", "0"], m = 5, n = 3
输出:4
解释:最多有 5 个 0 和 3 个 1 的最大子集是 {"10","0001","1","0"} ,因此答案是 4 。
其他满足题意但较小的子集包括 {"0001","1"} 和 {"10","1","0"} 。{"111001"} 不满足题意,因为它含 4 个 1 ,大于 n 的值 3 。

 

注意:

需要知道每个字符串的长度,同时选择的这几个字符串需要满足0 1 的个数满足 m n 

转换为背包问题

物品:strs

容量: m n

dp[m][n] 在 m n 的个数下,数量最大的情况

因为是数量,所以物品的value 统统为1

递推公式:

dp[x][y] = max(dp[x][y], 1+ dp[x-m_][y-n_]);

代码:

 1 // 要求:找出strs中找到一个子集,让所有这些子集的0和1的数目是多少
 2 // 
 3 // 难点:
 4 // 知道每个字符串0 1 的数目之后,然后匹配,让个数最大
 5 // 
 6 // dp[m][n] 当 MN 下最长的子集数目
 7 // 
 8 // dp[x][y] = 
 9 // 1,不选 dp[x][y]
10 // 2,选 dp[x][y] = 1 + dp[x-m][y-n] 
11 // 两个选则最大值 ——》其中因为是个数,所以他们的value 为1
12 //
13 int findMaxForm(vector<string>& strs, int m, int n) {
14     vector<vector<int>> dp(m + 1, vector<int>(n + 1, 0));
15 
16     dp[0][0] = 0;
17     for (int i = 0; i < strs.size(); i++)
18     {
19         int m_ = 0, n_ = 0;
20         for (char c : strs[i])
21         {
22             if (c == '0') m_++;
23             else if (c == '1') n_++;
24         }
25 
26         for (int x = m; x >= m_; x--)
27         {
28             for (int y = n; y >= n_; y--)
29             {
30                 dp[x][y] = max(dp[x][y], 1 + dp[x - m_][y - n_]);
31             }
32         }
33 
34     }
35 
36     return dp[m][n];
37 }

 

posted @ 2023-07-19 10:21  博二爷  阅读(9)  评论(0编辑  收藏  举报