文章目录
- [2809. 使数组和小于等于 x 的最少时间](https://leetcode.cn/problems/minimum-time-to-make-array-sum-at-most-x/)
- 思路:
- [2788. 按分隔符拆分字符串](https://leetcode.cn/problems/split-strings-by-separator/)
- 思路:
- [410. 分割数组的最大值](https://leetcode.cn/problems/split-array-largest-sum/)
- 思路:二分查找+贪心
2809. 使数组和小于等于 x 的最少时间
思路:
- 获取两个列表的长度n,并初始化一个二维数组f,用于存储最优解。
- 定义一个二维数组nums,用于存储输入的两个列表中的元素,并按照第二列元素进行排序。
- 使用动态规划的方法,通过遍历nums数组,计算最优解。其中,f[i][j]表示将前i个元素分配到j个集合中时的最大总和。通过比较f[i-1][j]和f[i-1][j-1]+a+b*j的大小,更新f[i][j]。
- 计算两个列表中所有元素的和,分别存储在s1和s2中。
- 遍历0至n的范围,判断是否满足条件s1 + s2 * j - f[n][j] <= x,若满足则返回j。
- 若没有满足条件的j值,则返回-1
class Solution {public int minimumTime(List<Integer> nums1, List<Integer> nums2, int x) {int n = nums1.size();int[][] f = new int[n + 1][n + 1]; // 定义二维数组f,用于存储最优解int[][] nums = new int[n][0]; // 定义二维数组nums,用于存储输入的两个列表中的元素for (int i = 0; i < n; ++i) {nums[i] = new int[] {nums1.get(i), nums2.get(i)}; // 将输入的两个列表中的元素按照相同的索引放入nums数组中}Arrays.sort(nums, Comparator.comparingInt(a -> a[1])); // 根据nums数组中第二列的元素进行排序for (int i = 1; i <= n; ++i) {for (int j = 0; j <= n; ++j) {f[i][j] = f[i - 1][j]; // 初始化f[i][j]为f[i-1][j]if (j > 0) { // 当j大于0时,进行以下操作int a = nums[i - 1][0], b = nums[i - 1][1];f[i][j] = Math.max(f[i][j], f[i - 1][j - 1] + a + b * j); // 更新f[i][j],取当前值与f[i-1][j-1]+a+b*j的较大值}}}int s1 = 0, s2 = 0;for (int v : nums1) {s1 += v; // 计算nums1列表中所有元素的和,存储在s1中}for (int v : nums2) {s2 += v; // 计算nums2列表中所有元素的和,存储在s2中}for (int j = 0; j <= n; ++j) {if (s1 + s2 * j - f[n][j] <= x) { // 判断是否满足条件 s1 + s2 * j - f[n][j] <= xreturn j; // 返回满足条件的j值}}return -1; // 若没有满足条件的j值,则返回-1}
}
2788. 按分隔符拆分字符串
思路:
- 对于每个单词,使用一个可变字符串
StringBuilder
来构建拆分后的单词。初始时,可变字符串为空。 - 遍历每个单词的每个字符,如果遇到指定的分隔符,就将可变字符串中的字符构成一个新的单词,并将其添加到结果列表中。然后清空可变字符串,准备构建下一个单词。
- 如果遇到的不是分隔符,则将当前字符添加到可变字符串中。
- 最后,如果可变字符串非空,则说明最后一个单词还没有添加到结果列表中,因此需要将其添加到结果列表中。
- 返回拆分后的结果列表。
//2788. 按分隔符拆分字符串public List<String> splitWordsBySeparator(List<String> words, char separator) {// 创建一个新的字符串列表,用于存储拆分后的结果List<String> res = new ArrayList<>();// 遍历原始字符串列表中的每个单词for (String word : words) {// 创建一个可变字符串,用于构建拆分后的单词StringBuilder sb = new StringBuilder();// 获取当前单词的长度int length = word.length();// 遍历当前单词的每个字符for (int i = 0; i < length; i++) {// 获取当前字符char c = word.charAt(i);// 如果当前字符是分隔符if (c == separator) {// 如果可变字符串不为空,则将其添加到结果列表中,并清空可变字符串if (sb.length() > 0) {res.add(sb.toString());sb.setLength(0);}} else {// 如果当前字符不是分隔符,则将其添加到可变字符串中sb.append(c);}}// 如果可变字符串不为空,则将其添加到结果列表中if (sb.length() > 0) {res.add(sb.toString());}}// 返回拆分后的字符串列表return res;}
410. 分割数组的最大值
思路:二分查找+贪心
利用二分查找法和贪心算法来求解将数组分割为m个非空连续子数组,使得每个子数组的和的最大值最小
- 首先,我们需要确定二分查找的左右边界。左边界
left
初始化为数组中的最大值,右边界right
初始化为数组所有元素的总和。 - 然后,我们使用二分查找法在左右边界之间查找满足条件的最小子数组和。
- 在每次迭代中,我们计算中间值
mid
,然后调用check
函数判断以mid
为目标值是否能将数组nums
分割成不超过m
个子数组。如果能,则将右边界更新为mid
,因为我们要寻找更小的子数组和。如果不能,则将左边界更新为mid + 1
,因为mid
不满足条件,我们需要尝试更大的值。 - 当左边界
left
不小于右边界right
时,二分查找结束,最终的结果即为左边界的值。 - 最后,返回左边界的值作为最小子数组和。
public int splitArray(int[] nums, int m) {int left = 0, right = 0;// 初始化左右边界for (int i = 0; i < nums.length; i++) {right += nums[i];if (left < nums[i]) {left = nums[i];}}// 使用二分查找法寻找最小的子数组和while (left < right) {int mid = (right - left) / 2 + left;if (check(nums, mid, m)) {right = mid;} else {left = mid + 1;}}// 返回最小的子数组和return left;
}public boolean check(int[] nums, int x, int m) {int sum = 0;int cnt = 1;// 统计满足条件的子数组个数for (int i = 0; i < nums.length; i++) {if (sum + nums[i] > x) {// 当前元素加上之后超过了目标值,需要分割出一个新的子数组cnt++;sum = nums[i];} else {// 将当前元素加入到当前子数组中sum += nums[i];}}// 判断最终的子数组个数是否小于等于目标个数return cnt <= m;
}
在check
函数中,遍历数组nums
,累加元素值到sum
变量中,如果累加和超过了目标值x
,则说明当前子数组和超过了目标值,需要分割出一个新的子数组,同时将子数组计数cnt
增加1,并将sum
重置为当前元素值。如果累加和未超过目标值,则将当前元素加入到当前子数组中。
最后,判断最终的子数组个数cnt
是否小于等于目标个数m
,如果是则返回true
,否则返回false
。
通过不断调整二分查找的左右边界,可以找到满足条件的最小子数组和
点击移步博客主页,欢迎光临~