LeetCode 1711. 大餐计数 做题小结

题目

大餐 是指 恰好包含两道不同餐品 的一餐,其美味程度之和等于 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

解题思路与算法

一. 暴力解法

显然,此题可以通过暴力解法解决:双层for循环找到所有满足要求的两数之和,并统计次数。时间复杂度为O(n^2), 超时。

二. 哈希表记录数字出现次数

通过暴力解法我们可以发现,在查找两数之和的第二个数时,用了O(n)时间(遍历整个数组)。又由于我们只关心次数,而并非具体的哪两个数,所以我们可以用哈希表记录数字与其出现次数,这样可以把查找时间优化到O(1)。注意到此题的输入限制,数字最大为2^20,所以我们可以在有限的空间枚举。代码如下:

   public int countPairs(int[] deliciousness) {
       Map<Integer, Integer> map = new HashMap<>();
       int mod = 1000000007;
       int answer = 0;
       int length = deliciousness.length;
       for (int num : deliciousness) {
           int powerOfTwo = 1;
           // 为什么是21? 因为数字最大为2^20, 2^20 + 2^20 = 2^21为可能的最大值,不可能再大啦!
           for (int i = 0; i <= 21; i++) {
               if (powerOfTwo >= num && map.containsKey(powerOfTwo - num)) {
                   answer += map.get(powerOfTwo - num);
                   answer %= mod;
               }
               powerOfTwo *= 2;
           }
           map.put(num, map.getOrDefault(num, 0) + 1);
       }
       return (int)answer;
   }

参考:https://leetcode-cn.com/problems/count-good-meals/solution/onshi-jian-fu-za-du-jie-fa-liang-shu-zhi-v0fo/

posted @ 2021-01-04 20:57  nmydt  阅读(195)  评论(0编辑  收藏  举报