1755. 最接近目标值的子序列和

给你一个整数数组 nums 和一个目标值 goal 。

你需要从 nums 中选出一个子序列,使子序列元素总和最接近 goal 。也就是说,如果子序列元素和为 sum ,你需要 最小化绝对差 abs(sum - goal) 。

返回 abs(sum - goal) 可能的 最小值 。

注意,数组的子序列是通过移除原始数组中的某些元素(可能全部或无)而形成的数组。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/closest-subsequence-sum
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。


import java.util.Arrays;


class Solution {

    public int minAbsDifference(int[] nums, int goal) {
        int half = nums.length >> 1;
        int[] f1 = new int[1 << half], f2 = new int[1 << (nums.length - half)];

        for (int i = 1; i < f1.length; ++i) {       //前半部分所有组合
            f1[i] = f1[i - Integer.lowestOneBit(i)] + nums[Integer.numberOfTrailingZeros(i)];
//            for (int j = 0; j < half; ++j) {
//                if ((i & (1 << j)) != 0) {
//                    /**
//                     * 常规写法,累加选中的每个数
//                     */
////                    f1[i] += nums[j];
//                    /**
//                     * 动态规划
//                     * 借助之前的累加和
//                     */
//                    f1[i] = f1[i ^ (1 << j)] + nums[j];
//                    break;
//                }
//            }
        }

        for (int i = 1; i < f2.length; ++i) {       //后半部分所有组合
            f2[i] = f2[i - Integer.lowestOneBit(i)] + nums[half + Integer.numberOfTrailingZeros(i)];
//            for (int j = 0; j < nums.length - half; ++j) {
//                if ((i & (1 << j)) != 0) {
////                    f2[i] += nums[half + j];
//                    f2[i] = f2[i ^ (1 << j)] + nums[half + j];
//                    break;
//                }
//            }
        }
        /**
         * 解法一:
         * 二分查找
         * 时间复杂度
         * n * (2^ n)
         */
//        Arrays.sort(f2);
//        int ans = Integer.MAX_VALUE;
//        for (int i = 0; i < f1.length; ++i) {       //枚举f1
//            int target = goal - f1[i];
//            int index = search(f2, target);    //二分查找f2
//            if (index < f2.length) {
//                ans = min(ans, abs(f1[i] + f2[index] - goal));
//            }
//            if (index - 1 >= 0) {
//                ans = min(ans, abs(f1[i] + f2[index - 1] - goal));
//            }
//        }

        /**
         * 解法二:
         * 双指针
         * 时间复杂度
         * 2 ^ n
         */
        Arrays.sort(f1);
        Arrays.sort(f2);
        int ans = Integer.MAX_VALUE;
        int left = 0, right = f2.length - 1;
        while (left < f1.length && right >= 0) {
            int sum = f1[left] + f2[right];
            ans = Math.min(ans, Math.abs(sum - goal));
            if (sum == goal) {
                /**
                 * 若0可以,则0一定是最优解
                 */
                break;
            } else if (sum > goal) {
                right--;
            } else {
                left++;
            }
        }
        return ans;
    }
}
posted @ 2022-02-09 17:03  Tianyiya  阅读(89)  评论(0)    收藏  举报