322. 零钱兑换
题目描述
给定不同面额的硬币 coins
和一个总金额 amount
。编写一个函数来计算可以凑成总金额所需的最少的硬币个数。如果没有任何一种硬币组合能组成总金额,返回 -1。
你可以认为每种硬币的数量是无限的。
力扣题目链接
322. 零钱兑换
解题思路
这个问题可以使用动态规划来解决。我们定义一个数组 dp
,其中 dp[i]
表示凑成金额 i
所需的最少硬币数量。初始化 dp[0]
为 0,因为凑成金额 0 所需的硬币数量为 0。其他 dp[i]
初始化为 Integer.MAX_VALUE
,表示初始状态下无法凑成金额 i
。
接下来,我们遍历每一种硬币,并更新 dp
数组。对于每一种硬币 coins[i]
,我们更新所有大于或等于 coins[i]
的金额 j
的最少硬币数量。具体来说,如果 dp[j - coins[i]]
不是初始值(即可以凑成金额 j - coins[i]
),那么 dp[j]
可以更新为 min(dp[j], dp[j - coins[i]] + 1)
。
最后,我们检查 dp[amount]
的值。如果它仍然是 Integer.MAX_VALUE
,则返回 -1,表示无法凑成总金额。否则,返回 dp[amount]
,即凑成总金额所需的最少硬币数量。
代码实现
class Solution {public int coinChange(int[] coins, int amount) {int[] dp = new int[amount+1];for(int i=1; i<amount+1; i++){dp[i] = Integer.MAX_VALUE;}for(int i=0; i<coins.length; i++){for(int j=coins[i]; j<amount+1; j++){if(dp[j-coins[i]] != Integer.MAX_VALUE) {dp[j] = Math.min(dp[j], dp[j - coins[i]] + 1);}}}return dp[amount] == Integer.MAX_VALUE ? -1 : dp[amount];}
}
279. 完全平方数
题目描述
给定正整数 n,找到若干个完全平方数(比如 1, 4, 9, 16, …)使得它们的和等于 n。你需要让组成和的完全平方数的个数最少。
给你一个整数 n ,返回和为 n 的完全平方数的最少数量。
完全平方数是一个整数,其值等于另一个整数的平方;换句话说,其值等于一个整数自乘的积。例如,1、4、9 和 16 都是完全平方数,而 3 和 11 不是。
力扣题目链接
279. 完全平方数
解题思路
这个问题同样可以使用动态规划来解决。我们定义一个数组 dp,其中 dp[i] 表示组成和为 i 的完全平方数的最少数量。初始化 dp[0] 为 0,因为组成和为 0 的完全平方数的最少数量为 0。其他 dp[i] 初始化为 Integer.MAX_VALUE,表示初始状态下无法组成和为 i。
接下来,我们遍历所有的完全平方数,并更新 dp 数组。对于每一个完全平方数 ii,我们更新所有大于或等于 ii 的和 j 的最少完全平方数数量。具体来说,如果 dp[j - ii] 不是初始值(即可以组成和为 j - ii),那么 dp[j] 可以更新为 min(dp[j], dp[j - i*i] + 1)。
最后,我们返回 dp[n],即组成和为 n 的完全平方数的最少数量。
代码实现
class Solution {public int numSquares(int n) {int max = Integer.MAX_VALUE;int[] dp = new int[n+1];for(int i=1; i<n+1; i++){dp[i] = max;}dp[0] = 0;for(int i=1; i*i<=n; i++){for(int j=i*i; j<n+1; j++){dp[j] = Math.min(dp[j], dp[j - i*i] + 1);}}return dp[n];}
}
139. 单词拆分
题目描述
给定一个非空字符串 s 和一个包含非空单词的列表 wordDict,判定 s 是否可以被空格拆分为一个或多个在字典中出现的单词。
说明:
- 拆分时可以重复使用字典中的单词。
- 你可以假设字典中没有重复的单词。
力扣题目链接
139. 单词拆分
解题思路
这个问题可以使用动态规划来解决。我们定义一个布尔数组 valid
,其中 valid[i]
表示字符串 s
的前 i
个字符是否可以由字典中的单词组成。
- 初始化
valid[0]
为true
,因为一个空字符串总是可以被拆分(不需要任何单词)。 - 遍历字符串
s
的每一个位置i
,对于每一个位置,我们再遍历它的每一个可能的前缀j
。 - 如果
s.substring(j, i)
是字典中的一个单词,并且valid[j]
为true
,那么我们可以说s.substring(0, i)
也可以被拆分,因此我们将valid[i]
设置为true
。 - 最后,
valid[s.length()]
将告诉我们整个字符串s
是否可以被拆分。
代码实现
class Solution {public boolean wordBreak(String s, List<String> wordDict) {HashSet<String> set = new HashSet<>(wordDict);boolean[] valid = new boolean[s.length() + 1];valid[0] = true;for (int i = 1; i <= s.length(); i++) {for (int j = 0; j < i && !valid[i]; j++) {if (set.contains(s.substring(j, i)) && valid[j]) {valid[i] = true;}}}return valid[s.length()];}
}
56. 携带矿石资源(第八期模拟笔试)
题目描述
你是一名宇航员,即将前往一个遥远的行星。在这个行星上,有许多不同类型的矿石资源,每种矿石都有不同的重要性和价值。你需要选择哪些矿石带回地球,但你的宇航舱有一定的容量限制。
给定一个宇航舱,最大容量为 C。现在有 N 种不同类型的矿石,每种矿石有一个重量 w[i],一个价值 v[i],以及最多 k[i] 个可用。不同类型的矿石在地球上的市场价值不同。你需要计算如何在不超过宇航舱容量的情况下,最大化你所能获取的总价值。
输入描述
输入共包括四行,第一行包含两个整数 C 和 N,分别表示宇航舱的容量和矿石的种类数量。
接下来的三行,每行包含 N 个正整数。具体如下:
- 第二行包含 N 个整数,表示 N 种矿石的重量。
- 第三行包含 N 个整数,表示 N 种矿石的价格。
- 第四行包含 N 个整数,表示 N 种矿石的可用数量上限。
解题思路
这个问题是一个典型的多重背包问题,可以通过动态规划来解决。我们可以使用一个一维数组 dp 来表示在不同容量下的最大价值。数组的索引表示当前背包的容量,而值表示在该容量下能够获得的最大价值。
- 初始化 dp 数组,所有值设为 0,因为开始时背包中没有物品,价值为 0。
- 遍历每一种矿石,对于每一种矿石,再遍历其数量上限。
- 对于每一种数量,使用完全背包的思路,更新 dp 数组。
- 最后,dp[bagWeight] 将包含最大价值。
代码实现
import java.util.Scanner;
class multi_pack{public static void main(String [] args) {Scanner sc = new Scanner(System.in);int bagWeight, n;bagWeight = sc.nextInt();n = sc.nextInt();int[] weight = new int[n];int[] value = new int[n];int[] nums = new int[n];for (int i = 0; i < n; i++) weight[i] = sc.nextInt();for (int i = 0; i < n; i++) value[i] = sc.nextInt();for (int i = 0; i < n; i++) nums[i] = sc.nextInt();int[] dp = new int[bagWeight + 1];for (int i = 0; i < n; i++) {for (int j = bagWeight; j >= weight[i]; j--) {for (int k = 1; k <= nums[i] && (j - k * weight[i]) >= 0; k++) {dp[j] = Math.max(dp[j], dp[j - k * weight[i]] + k * value[i]);}}}System.out.println(dp[bagWeight]);}
}