1711. 大餐计数

这篇博客讨论了如何在给定一个整数数组,表示餐品的美味程度,来计算能组成美味程度等于2的幂的大餐数量。由于直接遍历会导致超时,提出了使用哈希表来优化,将时间复杂度降至O(nlogC),其中C是2的20次方。博主分享了问题分析和解决方案,并提供了代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

难度:中等

大餐 是指 恰好包含两道不同餐品 的一餐,其美味程度之和等于 2 的幂。

你可以搭配 任意 两道餐品做一顿大餐。

给你一个整数数组 deliciousness ,其中 deliciousness[i] 是第 i​​​​​​​ 道餐品的美味程度,返回你可以用数组中的餐品做出的不同 大餐 的数量。结果需要对 109 + 7 取余。

注意,只要餐品下标不同,就可以认为是不同的餐品,即便它们的美味程度相同。

示例 1:

输入:deliciousness = [1,3,5,7,9]
输出:4
解释:大餐的美味程度组合为 (1,3) 、(1,7) 、(3,5) 和 (7,9) 。
它们各自的美味程度之和分别为 4 、8 、8 和 16 ,都是 2 的幂。

示例 2:

输入:deliciousness = [1,1,1,3,3,3,7]
输出:15
解释:大餐的美味程度组合为 3 种 (1,1) ,9 种 (1,3) ,和 3 种 (1,7) 。

提示:

  • 1 <= deliciousness.length <= 105
  • 0 <= deliciousness[i] <= 220

超出时间限制:

class Solution {
    public int countPairs(int[] deliciousness) {
        long sum = 0;
        for (int i = 0; i < deliciousness.length - 1; i++) {
            for (int j = i + 1; j < deliciousness.length; j++) {
                if (Integer.bitCount(deliciousness[i] + deliciousness[j]) == 1)
                    sum++;
            }
        }
        return Math.toIntExact(sum % (1000000000 + 7));
    }
}

超时again:

class Solution {
    public int countPairs(int[] deliciousness) {
        long sum = 0;
        int n = deliciousness.length;
        Map<Integer, Integer> dishes = new HashMap<>();
        for (int i = 0; i < n; i++) {
            dishes.put(deliciousness[i], dishes.getOrDefault(deliciousness[i], 0) + 1);
        }
        List<Integer> keySet = new ArrayList<>(dishes.keySet());
        //计算自身重复的方案,如 1 1 1
        for (int i = 0; i < keySet.size(); i++) {
            int num = keySet.get(i);
            if (dishes.get(num) > 1 && Integer.bitCount(2 * num) == 1)
                //Cn2=n!/(2!*(n-2)!)=n(n-1)/2
                sum += dishes.get(num) * (dishes.get(num) - 1) / 2;
        }
        for (int i = 0; i < keySet.size() - 1; i++) {
            for (int j = i + 1; j < keySet.size(); j++) {
                int A = keySet.get(i);
                int B = keySet.get(j);
                if (Integer.bitCount(A + B) == 1)
                    sum += dishes.get(A) * dishes.get(B);
            }
        }
        return Math.toIntExact(sum % (1000000000 + 7));
    }
}

分析:

方法一:哈希表
朴素的解法是遍历数组 \textit{deliciousness}deliciousness 中的每对元素,对于每对元素,计算两个元素之和是否等于 2 的幂。该解法的时间复杂度为 O(n^2)),会超出时间限制。

上述朴素解法存在同一个元素被重复计算的情况,因此可以使用哈希表减少重复计算,降低时间复杂度。具体做法是,使用哈希表存储数组中的每个元素的出现次数,遍历到数组 deliciousness 中的某个元素时,在哈希表中寻找与当前元素的和等于 2 的幂的元素个数,然后用当前元素更新哈希表。由于遍历数组时,哈希表中已有的元素的下标一定小于当前元素的下标,因此任意一对元素之和等于 2 的幂的元素都不会被重复计算。

令 maxVal 表示数组 deliciousness 中的最大元素,则数组中的任意两个元素之和都不会超过 maxVal×2。令 maxSum=maxVal×2,则任意一顿大餐的美味程度之和为不超过 maxSum 的某个 2 的幂。

对于某个特定的 2 的幂 sum,可以在 O(n)O(n) 的时间内计算数组 deliciousness 中元素之和等于sum 的元素对的数量。数组deliciousness 中的最大元素maxVal 满足maxVal≤C,其中 C=2^{20}
 ,则不超过maxSum 的 2 的幂有O(logmaxSum)=O(logmaxVal)=O(logC) 个,因此可以在 O(nlogC) 的时间内计算数组deliciousness 中的大餐数量。

出处:https://leetcode-cn.com/problems/count-good-meals/solution/da-can-ji-shu-by-leetcode-solution-fvg9/

代码:

class Solution {
    public int countPairs(int[] deliciousness) {
        final int MOD = 1000000007;
        int maxVal = 0;
        for (int val : deliciousness) {
            maxVal = Math.max(maxVal, val);
        }
        int maxSum = maxVal * 2;
        int pairs = 0;
        Map<Integer, Integer> map = new HashMap<Integer, Integer>();
        int n = deliciousness.length;
        for (int i = 0; i < n; i++) {
            int val = deliciousness[i];
            for (int sum = 1; sum <= maxSum; sum <<= 1) {
                int count = map.getOrDefault(sum - val, 0);
                pairs = (pairs + count) % MOD;
            }
            map.put(val, map.getOrDefault(val, 0) + 1);
        }
        return pairs;
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值