算法训练day43Leetcode1049. 最后一块石头的重量 II 494. 目标和 ● 474.一和零

1049 最后一块石头的重量

题目描述

有一堆石头,用整数数组 stones 表示。其中 stones[i] 表示第 i 块石头的重量。

每一回合,从中选出任意两块石头,然后将它们一起粉碎。假设石头的重量分别为 x 和 y,且 x <= y。那么粉碎的可能结果如下:

  • 如果 x == y,那么两块石头都会被完全粉碎;
  • 如果 x != y,那么重量为 x 的石头将会完全粉碎,而重量为 y 的石头新重量为 y-x

最后,最多只会剩下一块 石头。返回此石头 最小的可能重量 。如果没有石头剩下,就返回 0

示例 1:

输入:stones = [2,7,4,1,8,1]
输出:1
解释:
组合 2 和 4,得到 2,所以数组转化为 [2,7,1,8,1],
组合 7 和 8,得到 1,所以数组转化为 [2,1,1,1],
组合 2 和 1,得到 1,所以数组转化为 [1,1,1],
组合 1 和 1,得到 0,所以数组转化为 [1],这就是最优值。

示例 2:

输入:stones = [31,26,33,21,40]
输出:5

提示:

  • 1 <= stones.length <= 30
  • 1 <= stones[i] <= 100

题目分析 

如果使用一维dp数组,物品遍历的for循环放在外层,遍历背包的for循环放在内层,且内层for循环倒序遍历!

  1. 确定dp数组以及下标的含义

dp[j]表示容量(这里说容量更形象,其实就是重量)为j的背包,最多可以背最大重量为dp[j]

2.确定递推公式

01背包的递推公式为:dp[j] = max(dp[j], dp[j - weight[i]] + value[i]);

本题则是:dp[j] = max(dp[j], dp[j - stones[i]] + stones[i]);

最后dp[target]里是容量为target的背包所能背的最大重量。

那么分成两堆石头,一堆石头的总重量是dp[target],另一堆就是sum - dp[target]。

在计算target的时候,target = sum / 2 因为是向下取整,所以sum - dp[target] 一定是大于等于dp[target]的

那么相撞之后剩下的最小石头重量就是 (sum - dp[target]) - dp[target]。

acm模式代码

#include <iostream>
#include <vector>
#include <algorithm>

class Solution {
public:
    int lastStoneWeightII(std::vector<int>& stones) {
        std::vector<int> dp(1501, 0);
        int sum = 0;
        for (int i = 0; i < stones.size(); i++) {
            sum += stones[i];
        }
        int target = sum / 2;
        for (int i = 0; i < stones.size(); i++) { //遍历物品
            for (int j = target; j >= stones[i]; j--) { //遍历背包
                dp[j] = std::max(dp[j], dp[j - stones[i]] + stones[i]);
            }
        }
        int last = sum - dp[target] - dp[target];
        //打印dp
        // for (int i = 0; i < sum; i ++) {
        //     std::cout << dp[i] << " " ;
        // }
        // std::cout << std::endl;
        return last;
    }
};

int main() {
    Solution sol;
    std::vector<int> stones = {2,7,4,1,8,1};
    int last = sol.lastStoneWeightII(stones);
    std::cout << "last:" << last << std::endl;
    return 0;
}

494 目标和

题目描述

给定一个非负整数数组,a1, a2, ..., an, 和一个目标数,S。现在你有两个符号 + 和 -。对于数组中的任意一个整数,你都可以从 + 或 -中选择一个符号添加在前面。

返回可以使最终数组和为目标数 S 的所有添加符号的方法数。

示例:

  • 输入:nums: [1, 1, 1, 1, 1], S: 3
  • 输出:5

解释:

  • -1+1+1+1+1 = 3
  • +1-1+1+1+1 = 3
  • +1+1-1+1+1 = 3
  • +1+1+1-1+1 = 3
  • +1+1+1+1-1 = 3

一共有5种方法让最终目标和为3。

提示:

  • 数组非空,且长度不会超过 20 。
  • 初始的数组的和不会超过 1000 。
  • 保证返回的最终结果能被 32 位整数存下。

题目分析

既然为target,那么就一定有 left组合 - right组合 = target。

left + right = sum,而sum是固定的。right = sum - left

公式来了, left - (sum - left) = target 推导出 left = (target + sum)/2 。

target是固定的,sum是固定的,left就可以求出来。

例如:dp[j],j 为5,

  • 已经有一个1(nums[i]) 的话,有 dp[4]种方法 凑成 容量为5的背包。
  • 已经有一个2(nums[i]) 的话,有 dp[3]种方法 凑成 容量为5的背包。
  • 已经有一个3(nums[i]) 的话,有 dp[2]中方法 凑成 容量为5的背包
  • 已经有一个4(nums[i]) 的话,有 dp[1]中方法 凑成 容量为5的背包
  • 已经有一个5 (nums[i])的话,有 dp[0]中方法 凑成 容量为5的背包

那么凑整dp[5]有多少方法呢,也就是把 所有的 dp[j - nums[i]] 累加起来。

所以求组合类问题的公式,都是类似这种:

dp[j] += dp[j - nums[i]]

474 1和0

题目描述 

给你一个二进制字符串数组 strs 和两个整数 m 和 n 。

请你找出并返回 strs 的最大子集的长度,该子集中 最多 有 m 个 0 和 n 个 1 。

如果 x 的所有元素也是 y 的元素,集合 x 是集合 y 的 子集 。

示例 1:

输入: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 。

示例 2:

输入:strs = ["10", "0", "1"], m = 1, n = 1
输出:2
解释:最大的子集是 {"0", "1"} ,所以答案是 2 。

提示:

  • 1 <= strs.length <= 600
  • 1 <= strs[i].length <= 100
  • strs[i] 仅由 '0' 和 '1' 组成
  • 1 <= m, n <= 100

题目分析

本题中strs 数组里的元素就是物品,每个物品都是一个!

而m 和 n相当于是一个背包,两个维度的背包

确定递推公式

dp[i][j] 可以由前一个strs里的字符串推导出来,strs里的字符串有zeroNum个0,oneNum个1。

dp[i][j] 就可以是 dp[i - zeroNum][j - oneNum] + 1。

然后我们在遍历的过程中,取dp[i][j]的最大值。

所以递推公式:dp[i][j] = max(dp[i][j], dp[i - zeroNum][j - oneNum] + 1);

可以回想一下01背包的递推公式:dp[j] = max(dp[j], dp[j - weight[i]] + value[i]);

对比一下就会发现,字符串的zeroNum和oneNum相当于物品的重量(weight[i]),字符串本身的个数相当于物品的价值(value[i])。

内层for循环遍历背包容量且从后向前遍历!

那么本题也是,物品就是strs里的字符串,背包容量就是题目描述中的m和n。

acm模式代码

#include <iostream>
#include <vector>
#include <string>
#include <algorithm>

class Solution {
public:
    int findMaxForm(std::vector<std::string>& strs, int m, int n) {
        // 初始化动态规划数组
        std::vector<std::vector<int>> dp(m + 1, std::vector<int>(n + 1, 0));
        // 遍历字符串数组
        for (std::string str: strs) {
            int oneNum = 0, zeroNum = 0;
            // 计算当前字符串中0和1的数量
            for (char c: str) {
                if (c == '0') zeroNum++;
                else oneNum++;
            }
            // 更新动态规划数组
            for (int i = m; i >= zeroNum; i--) {
                for (int j = n; j >= oneNum; j--) {
                    dp[i][j] = std::max(dp[i][j], dp[i - zeroNum][j - oneNum] + 1);
                }
            }
        }
        // 返回最终结果
        return dp[m][n];
    }
};

int main() {
    Solution solution;
    std::vector<std::string> strs = {"10", "0001", "111001", "1", "0"};
    int m = 5, n = 3;
    int maxForm = solution.findMaxForm(strs, m, n);
    std::cout << "Maximum number of strings that can be formed: " << maxForm << std::endl;
    return 0;
}

最近更新

  1. TCP协议是安全的吗?

    2024-03-14 21:06:04       18 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-03-14 21:06:04       19 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-03-14 21:06:04       18 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-03-14 21:06:04       20 阅读

热门阅读

  1. Python中的函数定义与使用

    2024-03-14 21:06:04       21 阅读
  2. 海豚调度系列之:集群部署(Cluster)

    2024-03-14 21:06:04       18 阅读
  3. L1-039 古风排版(C++)

    2024-03-14 21:06:04       21 阅读
  4. C#在字段中存储数据

    2024-03-14 21:06:04       21 阅读
  5. 如何使用vue定义组件之——全局or局部

    2024-03-14 21:06:04       24 阅读
  6. Spring核心接口:ObjectProvider接口

    2024-03-14 21:06:04       21 阅读
  7. MyBatis-Plus知识点(一)

    2024-03-14 21:06:04       21 阅读
  8. 企业跨境出海选择AWS怎么样?

    2024-03-14 21:06:04       20 阅读
  9. leetcode88--合并两个有序数组

    2024-03-14 21:06:04       25 阅读
  10. intel至强系列CPU以及介绍

    2024-03-14 21:06:04       19 阅读
  11. python中判断是否是数字

    2024-03-14 21:06:04       20 阅读