You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

89 lines
3.6 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

package class03;
import java.util.Arrays;
// 本题测试链接 : https://leetcode.com/problems/closest-subsequence-sum/
// 本题数据量描述:
// 1 <= nums.length <= 40
// -10^7 <= nums[i] <= 10^7
// -10^9 <= goal <= 10^9
// 通过这个数据量描述可知,需要用到分治,因为数组长度不大
// 而值很大,用动态规划的话,表会爆
public class Code06_ClosestSubsequenceSum {
// 数组左半部分的所有可能累加和都放在这里
public static int[] l = new int[1 << 20];
// 数组右半部分的所有可能累加和都放在这里
public static int[] r = new int[1 << 20];
public static int minAbsDifference(int[] nums, int goal) {
if (nums == null || nums.length == 0) {
return goal;
}
// 收集数组左半部分,所有可能的累加和
// 并且返回一共收集了几个数就是le
int le = process(nums, 0, nums.length >> 1, 0, 0, l);
// 收集数组右半部分,所有可能的累加和
// 并且返回一共收集了几个数就是re
int re = process(nums, nums.length >> 1, nums.length, 0, 0, r);
// 把左半部分收集到的累加和排序
Arrays.sort(l, 0, le);
// 把左半部分收集到的累加和排序
Arrays.sort(r, 0, re--);
// 为什么要排序?
// 因为排序之后定位数字可以不回退
// 比如你想累加和尽量接近10
// 左半部分累加和假设为 : 0, 2, 3, 7, 9
// 右半部分累加和假设为 : 0, 6, 7, 8, 9
// 左部分累加和是0时右半部分选哪个累加和最接近10是9
// 左部分累加和是2时右半部分选哪个累加和最接近10是8
// 左部分累加和是3时右半部分选哪个累加和最接近10是7
// 左部分累加和是7时右半部分选哪个累加和最接近10是0
// 左部分累加和是9时右半部分选哪个累加和最接近10是0
// 上面你可以看到,
// 当你从左往右选择左部分累加和时,右部分累加和的选取,可以从右往左
// 这就非常的方便
// 下面的代码就是这个意思
int ans = Math.abs(goal);
for (int i = 0; i < le; i++) {
int rest = goal - l[i];
while (re > 0 && Math.abs(rest - r[re - 1]) <= Math.abs(rest - r[re])) {
re--;
}
ans = Math.min(ans, Math.abs(rest - r[re]));
}
return ans;
}
// 解释一下
// nums[0..index-1]已经选了一些数字组成了累加和sum
// 当前来到nums[index....end)这个范围,所有可能的累加和
// 填写到arr里去
// fill参数的意思是: 如果出现新的累加和填写到arr的什么位置
// 返回所有生成的累加和现在填到了arr的什么位置
public static int process(int[] nums, int index, int end, int sum, int fill, int[] arr) {
if (index == end) { // 到了终止为止了,该结束了
// 把当前的累加和sum
// 填写到arr[fill]的位置
// 然后fill++,表示如果后续再填的话
// 该放在什么位置了
arr[fill++] = sum;
} else {
// 可能性1 : 不要当前的数字
// 走一个分支形成多少累加和都填写到arr里去
// 同时返回这个分支把arr填到了什么位置
fill = process(nums, index + 1, end, sum, fill, arr);
// 可能性2 : 要当前的数字
// 走一个分支形成多少累加和都填写到arr里去
// 接着可能性1所填到的位置继续填写到arr里去
// 这就是为什么要拿到上一个分支填到哪了
// 因为如果没有这个信息可能性2的分支不知道往哪填生成的累加和
fill = process(nums, index + 1, end, sum + nums[index], fill, arr);
}
// 可能性1 + 可能性2总共填了多少都返回
return fill;
}
}