1. 问题描述:
大餐是指恰好包含两道不同餐品的一餐,其美味程度之和等于 2 的幂。你可以搭配任意两道餐品做一顿大餐。给你一个整数数组 deliciousness ,其中 deliciousness[i] 是第 i 道餐品的美味程度,返回你可以用数组中的餐品做出的不同大餐的数量。结果需要对 10 ^ 9 + 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 <= 10^5
0 <= deliciousness[i] <= 2^20
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/count-good-meals
2. 思路分析:
① 分析题目可以知道我们需要在deliciousness数组中找到所有两个数字相加和为2的n次幂的组合数目,所以这道题目本质上求解的是两个数字相加的和为某个target的的数目,只是这里求解的target存在多个,由题目可以知道deliciousness[i]的最大值是2 ^ 20,所以两个数字之和最大是2 ^ 21,需要尝试求解的target为2的0次幂到2的21次幂,一开始想到的是哈希表来进行计数(使用哈希表来寻找和为当前的2的i次幂的另外一个数字是否存在),因为使用的是python语言所以使用字典来进行计数,遍历字典中的键值对,对于当前的键,寻找和为2 ^ 0~2 ^ 21所有2的i次幂的另外一个数字是否存在,如果存在说明两个数字的和相加为当前的2 ^ i次幂,并且这里需要注意的是需要分两种情况进行判断:第一种情况是当另外一个数字等于当前遍历的键的时候并且两者相加的和为2 ^ i次幂这个时候累加的结果为(dic[key] * dic[key] - 1) // 2,具体的例子:当前的键为1,寻找的另外一个数字也为1,这个时候两个数字相加等于2的1次幂,当前的键为2另外一个数字也为2,这个时候两数相加等于2 ^ 2,这个时候的组合数目应该等于等差数列的求和,也就是(dic[key] * dic[key] - 1) // 2,第二种情况是两个相加的数字不相等的时候那么组合的数目等于两者在字典中对应的数目相乘,因为是遍历字典中所有的值所以当两个相加的数字不相等的时候重复计算了一次组合的数目,所以需要将两个加数不等的组合数目的结果除以2
② 除了①中使用哈希表来寻找两个数字相加的和为target的思路之外,还可以使用双指针的方法进行解决,这个与之前的两数之和等于target的本质上是一样的,通过对deliciousness数组的元素进行计数并且对其进行去重之后排序然后使用双指针的方法,具体的实现其实也不复杂我们可以依次计算deliciousness数组中是否存在两数之和等于2 ^ 0~2 ^ 21次这个范围的数字,通过双指针来计算对应的组合数目即可
3. 代码如下:
字典:
import collections
from typing import List
class Solution:
def countPairs(self, deliciousness: List[int]) -> int:
# 通过哈希表寻找和为target的另外一个数字是否存在
dic = collections.defaultdict(int)
for n in deliciousness:
dic[n] += 1
res, count, mod = 0, 0, 10 ** 9 + 7
for key, value in dic.items():
for i in range(22):
# 两个相加的数字相等并且等于当前的2的i次幂
if (1 << i) - key == key:
count += value * (value - 1) // 2 % mod
# 两个数字不等的情况而且等于当前的2的i次幂
elif (1 << i) - key in dic:
res = (res + value * dic[(1 << i) - key]) % mod
# 两个加数不等的时候重复计算了一次所以需要除以2
return (res // 2 + count) % mod
双指针:
import collections
from typing import List
class Solution:
def countPairs(self, deliciousness: List[int]) -> int:
mod = 10 ** 9 + 7
dic = collections.defaultdict(int)
for n in deliciousness:
dic[n] += 1
deliciousness = list(set(deliciousness))
deliciousness.sort()
i, res = 0, 0
# 尝试2的零次方~最后一个元素的两倍
while 2 ** i <= deliciousness[-1] * 2:
l, r = 0, len(deliciousness) - 1
while l <= r:
if deliciousness[l] + deliciousness[r] < 2 ** i:
l += 1
elif deliciousness[l] + deliciousness[r] > 2 ** i:
r -= 1
else:
# 当最后两个元素相等的时候而且等于了当前的2的i次幂那么肯定应该需要特殊处理
if l == r:
res = (res + dic[deliciousness[l]] * (dic[deliciousness[l]] - 1) // 2) % mod
break
else:
res = (res + dic[deliciousness[l]] * dic[deliciousness[r]]) % mod
r -= 1
l += 1
i += 1
return res