2021-03-06

该博客详细解析了LeetCode第1755题的解决方案,采用暴力枚举法,将数组分成两部分,分别计算子序列和,然后寻找最接近目标值的组合。博主通过计算所有可能的子序列和,再通过二分查找在另一部分中找到匹配项,从而找到最小差值。这种方法适用于长度较小的数组,对于长度不超过40的数组,效率较高。
摘要由CSDN通过智能技术生成

leetcode1755 题解

题目

给你一个整数数组 n u m s nums nums 和一个目标值 g o a l goal goal
你需要从 n u m s nums nums 中选出一个子序列,使子序列元素总和最接近 g o a l goal goal 。也就是说,如果子序列元素和为 s u m sum sum ,你需要 最小化绝对差 a b s ( s u m − g o a l ) abs(sum - goal) abs(sumgoal)
返回 a b s ( s u m − g o a l ) abs(sum - goal) abs(sumgoal) 可能的 最小值 。
注意,数组的子序列是通过移除原始数组中的某些元素(可能全部或无)而形成的数组。

提示:
1 < = n u m s . l e n g t h < = 40 1 <= nums.length <= 40 1<=nums.length<=40
− 107 < = n u m s [ i ] < = 107 -107 <= nums[i] <= 107 107<=nums[i]<=107
− 109 < = g o a l < = 109 -109 <= goal <= 109 109<=goal<=109

题解

看到题目中nums长度为0~40,可以通过暴力枚举的方法解决这道题。首先将数组一分为二,这样的话数组前半部分和后半部分的最大长度不会超过20。假设其中一个数组的长度为n,那么数组可能的子序列的排列会有 2 n 2^n 2n种。显然区间 [ 0 , 2 n ) [0, 2^n) [0,2n)中的每一个整数代表子序列的一种排列,因为假设 0 < = i < 2 n 0<=i<2^n 0<=i<2n的二进制表示为 b 1 , b 2 , ⋅ ⋅ ⋅ , b n b_1, b_2, ···,b_n b1,b2,,bn,则 b i b_i bi为1表示选择数组中的元素i,为0表示不选择数组中的元素i。因此我们可以通过以下代码计算nums所有子序列的和:

public static int[] sum(int[] nums) {
	int[] ans = new int[1 << nums.length];
	for ( int i = 0; i < ans.legth; i++) {
		for(int j = 0; j < nums.length; j++) {
			if(i & (1 << j) == 1) ans[i] += nums[j];
		}
	}
	return ans;
}

求出了nums前半部分数组和后半部分数组的子序列和之后,满足abs(sum - goal)的子序列有三种情况:

  • nums前半部分数组中;
  • nums后半部分数组中;
  • 横跨整个nums数组;

假设nums数组前半部分子序列和保存在left中,后半部分子序列和保存在right中,我们对right进行排序,之后对于left中的每一个元素,在right中寻找与之相加等于goal的元素;

public int minAbsDifference(int[] nums, int goal) {
	int[] left = sum(Arrays.copyOfRange(nums, 0, nums.length / 2);
	int[] right = sum(Arrays.copyOfRange(nums, nums.length / 2, nums.length);
	Arrays.sort(right);
	int ans = Integer.MAX_VALUE;
	for (int i = 0; i < left.length; i++) {
		int target = goal - f1[i];
		int index = Arrays.binarySearch(right, target);
		if(index > 0) return 0;
		else index = -index - 1;
		if (index < right.length) {
            ans = min(ans, abs(left[i] + left[index] - goal));
        }
        if (index - 1 >= 0) {
            ans = min(ans, abs(right[i] + right[index - 1] - goal));
        }
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值