当前位置: 首页 > news >正文

Leetcode+Java+动态规划IV

494.目标和

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

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

  • 例如,nums = [2, 1] ,可以在 2 之前添加 '+' ,在 1 之前添加 '-' ,然后串联起来得到表达式 "+2-1" 。

返回可以通过上述方法构造的、运算结果等于 target 的不同 表达式 的数目。

示例 1:

输入:nums = [1,1,1,1,1], target = 3
输出:5
解释:一共有 5 种方法让最终目标和为 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
+1 + 1 + 1 + 1 - 1 = 3

示例 2:

输入:nums = [1], target = 1
输出:1

提示:

  • 1 <= nums.length <= 20
  • 0 <= nums[i] <= 1000
  • 0 <= sum(nums[i]) <= 1000
  • -1000 <= target <= 1000

原理

  • 问题分解
    • 转化为 0-1 背包问题:
      • 物品为 nums[i],重量和价值均为 nums[i]。
      • 背包容量为 bagSize = (sum + target) / 2。
      • 目标是计算和为 bagSize 的子集方案数。
    • 定义 dp[j] 为和为 j 的子集方案数。
    • 状态转移:
      • 对于每个数字 nums[i]:
        • 不选:dp[j] 不变。
        • 选(需 j >= nums[i]):dp[j] += dp[j - nums[i]](累加方案数)。
    • 使用一维数组,从大到小遍历 j,确保 dp[j - nums[i]] 使用上一轮状态。
  • 初始化
    • dp[0] = 1:空子集和为 0,有 1 种方案。
    • 其他 dp[j] = 0(默认)。
  • 边界条件
    • 如果 |target| > sum,无解,返回 0。
    • 如果 (sum + target) % 2 == 1,无整数解,返回 0。
    • 如果 bagSize < 0,无解(由 |target| <= sum 保证)。
  • 时间和空间复杂度
    • 时间复杂度:O(n * bagSize),n 为数组长度,bagSize <= (sum + target) / 2 <= 2000。
    • 空间复杂度:O(bagSize),使用一维 DP 数组。

代码

class Solution {public int findTargetSumWays(int[] nums, int target) {// 计算总和int sum = 0;for (int num : nums) {sum += num;}// 检查边界条件if (Math.abs(target) > sum || (sum + target) % 2 == 1) {return 0;}// 计算背包容量int bagSize = (sum + target) / 2;// 创建 DP 数组int[] dp = new int[bagSize + 1];dp[0] = 1;// 遍历每个数字for (int num : nums) {if (num > bagSize) continue; // 跳过无效数字for (int j = bagSize; j >= num; j--) {dp[j] += dp[j - num];}}return dp[bagSize];}
}

474.一和零

给你一个二进制字符串数组 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

原理

  • 问题分解
    • 定义 dp[j][k] 为在最多 j 个 0 和 k 个 1 的限制下,能选择的最大字符串数量(子集长度)。
    • 对于每个字符串(有 zeros 个 0 和 ones 个 1):
      • 不选:dp[j][k] 不变。
      • 选(需 j >= zeros 且 k >= ones):dp[j][k] = max(dp[j][k], dp[j - zeros][k - ones] + 1)。
    • 状态转移:
      • dp[j][k] = max(dp[j][k], dp[j - zeros][k - ones] + 1)。
  • 初始化
    • dp[0][0] = 0(无 0 和 1 时,子集长度为 0)。
    • 其他 dp[j][k] 默认初始化为 0(Java 数组默认值)。
  • 遍历顺序
    • 使用二维 DP 数组,需从大到小遍历 j 和 k,以避免覆盖上一轮的状态(类似一维 0-1 背包的逆序遍历)。
    • 每个字符串的 zeros 和 ones 通过遍历字符计算。
  • 时间和空间复杂度
    • 时间复杂度:O(L + N * m * n),其中:
      • L 是 strs 中所有字符串的总长度(计算 zeros 和 ones,L <= 600 * 100 = 6 * 10^4)。
      • N 是字符串数量(N <= 600),m * n 是 DP 数组遍历的次数(m, n <= 100)。
    • 空间复杂度:O(m * n),使用 m+1 * n+1 的 DP 数组。

代码

class Solution {public int findMaxForm(String[] strs, int m, int n) {// 创建二维 DP 数组int[][] dp = new int[m + 1][n + 1];// 预计算每个字符串的 0 和 1 数量int[] zeros = new int[strs.length];int[] ones = new int[strs.length];for (int i = 0; i < strs.length; i++) {for (char c : strs[i].toCharArray()) {if (c == '0') zeros[i]++;else ones[i]++;}}// 遍历每个字符串for (int i = 0; i < strs.length; i++) {if (zeros[i] > m || ones[i] > n) continue;// 从大到小遍历for (int j = m; j >= zeros[i]; j--) {for (int k = n; k >= ones[i]; k--) {dp[j][k] = Math.max(dp[j][k], dp[j - zeros[i]][k - ones[i]] + 1);}}}return dp[m][n];}
}

完全背包理论基础

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

小明的行李箱所能承担的总重量是有限的,问小明应该如何抉择,才能携带最大价值的研究材料,每种研究材料可以选择无数次,并且可以重复选择。

输入描述
第一行包含两个整数,n,v,分别表示研究材料的种类和行李所能承担的总重量 

接下来包含 n 行,每行两个整数 wi 和 vi,代表第 i 种研究材料的重量和价值

输出描述
输出一个整数,表示最大价值。
输入示例
4 5
1 2
2 4
3 4
4 5
输出示例
10
提示信息
第一种材料选择五次,可以达到最大值。

数据范围:

1 <= n <= 10000;
1 <= v <= 10000;
1 <= wi, vi <= 10^9.

原理

  • 问题分解
    • 定义 dp[i][j] 为前 i 种物品(索引 0 到 i-1)在容量 j 下的最大价值。
    • 状态转移:
      • 不选第 i-1 种物品:dp[i][j] = dp[i - 1][j]。
      • 选第 i-1 种物品(可多次):dp[i][j] = max(dp[i][j], dp[i][j - weight[i-1]] + value[i-1])。
    • 合并为:dp[i][j] = max(dp[i - 1][j], dp[i][j - weight[i-1]] + value[i-1])(if j >= weight[i-1])。
  • 初始化
    • dp[0][j] = k * value[0],其中 k = j / weight[0](第一种物品可重复选择)。
    • dp[i][0] = 0(容量为 0,价值为 0)。
  • 一维 DP 优化
    • 由于每种物品可重复选择,可以用一维数组 dp[j],表示容量 j 下的最大价值。
    • 状态转移:dp[j] = max(dp[j], dp[j - weight[i]] + value[i])(if j >= weight[i])。
    • 遍历 j 从小到大(与 0-1 背包相反),因为需要使用当前物品的最新状态。
  • 时间和空间复杂度
    • 二维 DP
      • 时间复杂度:O(n * v),遍历 n 种物品和 v 个容量。
      • 空间复杂度:O(n * v)。
    • 一维 DP
      • 时间复杂度:O(n * v)。
      • 空间复杂度:O(v)。

代码

import java.util.Scanner;public class Main {public static void main(String[] args) {Scanner scanner = new Scanner(System.in);// 读取种类 n 和容量 vint n = scanner.nextInt();int v = scanner.nextInt();// 创建数组存储重量和价值int[] weight = new int[n];long[] value = new long[n]; // 使用 long 存储价值// 读取输入for (int i = 0; i < n; i++) {weight[i] = scanner.nextInt();value[i] = scanner.nextLong();}// 创建二维 DP 数组long[][] dp = new long[n][v + 1];// 初始化第一种物品for (int j = 0; j <= v; j++) {dp[0][j] = (j / weight[0]) * value[0]; // 尽量多次选择第一种物品}// 动态规划for (int i = 1; i < n; i++) {for (int j = 0; j <= v; j++) {// 不选第 i 种物品dp[i][j] = dp[i - 1][j];// 选第 i 种物品(可多次)if (j >= weight[i]) {dp[i][j] = Math.max(dp[i][j], dp[i][j - weight[i]] + value[i]);}}}System.out.println(dp[n - 1][v]);scanner.close();}
}

518.零钱兑换II

给你一个整数数组 coins 表示不同面额的硬币,另给一个整数 amount 表示总金额。

请你计算并返回可以凑成总金额的硬币组合数。如果任何硬币组合都无法凑出总金额,返回 0 。

假设每一种面额的硬币有无限个。 

题目数据保证结果符合 32 位带符号整数。

    示例 1:

    输入:amount = 5, coins = [1, 2, 5]
    输出:4
    解释:有四种方式可以凑成总金额:
    5=5
    5=2+2+1
    5=2+1+1+1
    5=1+1+1+1+1
    

    示例 2:

    输入:amount = 3, coins = [2]
    输出:0
    解释:只用面额 2 的硬币不能凑成总金额 3 。
    

    示例 3:

    输入:amount = 10, coins = [10] 
    输出:1
    

    提示:

    • 1 <= coins.length <= 300
    • 1 <= coins[i] <= 5000
    • coins 中的所有值 互不相同
    • 0 <= amount <= 5000

    原理

    • 问题分解
      • 定义 dp[j] 为凑成金额 j 的组合数。
      • 状态转移:
        • 对于每种硬币 coins[i]:
          • 不使用该硬币:dp[j] 不变。
          • 使用该硬币(需 j >= coins[i]):dp[j] += dp[j - coins[i]](累加组合数)。
      • 遍历 j 从小到大(完全背包特性),因为需要使用当前硬币的最新状态。
    • 初始化
      • dp[0] = 1:金额 0 有 1 种组合(空组合)。
      • 其他 dp[j] = 0(默认)。
    • 时间和空间复杂度
      • 时间复杂度:O(n * amount),遍历 n 种硬币,每种硬币检查最多 amount 个金额。
      • 空间复杂度:O(amount),使用一维 DP 数组。
    • 数据范围
      • 1 <= coins.length <= 300
      • 1 <= coins[i] <= 5000
      • 0 <= amount <= 5000
      • 结果符合 32 位带符号整数(2^31 - 1 ≈ 2.1 * 10^9)。

    代码

    class Solution {public int change(int amount, int[] coins) {// 创建一维 DP 数组int[] dp = new int[amount + 1];// 初始化:金额 0 有 1 种组合dp[0] = 1;// 遍历每种硬币for (int coin : coins) {// 从 coin 开始遍历金额for (int j = coin; j <= amount; j++) {dp[j] += dp[j - coin];}}return dp[amount];}
    }

    http://www.dtcms.com/a/353454.html

    相关文章:

  • electron离线开发环境变量ELECTRON_OVERRIDE_DIST_PATH
  • [MT6835] MT6835开启secureboot导致写入分区提示Security deny for [customer].
  • 【图像算法 - 25】基于深度学习 YOLOv11 与 OpenCV 实现人员跌倒识别系统(人体姿态估计版本)
  • 达梦数据库配置文件-COMPATIBLE_MODE
  • LangChain4j入门一:LangChain4j简介及核心概念
  • 路由基础(一):IP地址规划
  • 基于单片机空调温度控制测温ds18b20系统Proteus仿真(含全部资料)
  • React 组件命名规范:为什么必须大写首字母蛊傲
  • HarmonyOS安全开发实战:一套好用的数据加密方案
  • SQL Server 批量插入脚本
  • 中資券商櫃台系統搭建與開發技術分析
  • AI 大模型 “幻觉” 难题:为何会 “一本正经地说错话”,如何让它更靠谱?
  • 基于Cursor AI IDE的Vue3留言板系统实战:从零搭建到智能优化全流程
  • java数据类型获取长度方式总结
  • 【C++】C++11新特性第一弹(列表初始化、新式声明、范围for和STL中的变化)
  • Flutter 开发技巧 AI 快速构建 json_annotation model 的提示词
  • 使用git上传项目的步骤
  • 煤矸石检测数据集VOC+YOLO格式3090张2类别
  • This method can cause UI unresponsiveness if invoked on the main thread.
  • week5-[字符数组]数和
  • 【技巧】PostgreSQL自动转换类型 int转bool 转int
  • 苍穹外卖项目实战(日记十一)-记录实战教程及问题的解决方法-(day3-3)完善菜品分页查询功能
  • 怎么理解API?
  • 用户体验设计 | 什么是 AX?从 UX 到 AX 的演进
  • 数据结构——算法效率的度量(时间复杂度与空间复杂度)
  • Elasticsearch 启动反复重启排查实录:从“内存不足”到“vm.max\_map\_count 过小”
  • 图表可视化地理趋势-Telerik WPF Chart
  • 智能汽车制造:海康NVR管理平台/工具EasyNVR多品牌NVR管理工具/设备实现无插件视频监控直播方案
  • R语言贝叶斯方法在生态环境领域中的技术应用
  • 攻克 Java 分布式难题:并发模型优化与分布式事务处理实战指南