题目描述
给你两个正整数数组 spells 和 potions ,长度分别为 n 和 m ,其中 spells[i] 表示第 i 个咒语的能量强度,potions[j] 表示第 j 瓶药水的能量强度。
同时给你一个整数 success 。一个咒语和药水的能量强度 相乘 如果 大于等于 success ,那么它们视为一对 成功 的组合。
请你返回一个长度为 n 的整数数组pairs,其中pairs[i] 是能跟第 i 个咒语成功组合的 药水 数目。
示例 1:
输入:spells = [5,1,3], potions = [1,2,3,4,5], success = 7
输出:[4,0,3]
解释:
- 第 0 个咒语:5 * [1,2,3,4,5] = [5,10,15,20,25] 。总共 4 个成功组合。
- 第 1 个咒语:1 * [1,2,3,4,5] = [1,2,3,4,5] 。总共 0 个成功组合。
- 第 2 个咒语:3 * [1,2,3,4,5] = [3,6,9,12,15] 。总共 3 个成功组合。
所以返回 [4,0,3] 。
示例 2:
输入:spells = [3,1,2], potions = [8,5,8], success = 16
输出:[2,0,2]
解释:
- 第 0 个咒语:3 * [8,5,8] = [24,15,24] 。总共 2 个成功组合。
- 第 1 个咒语:1 * [8,5,8] = [8,5,8] 。总共 0 个成功组合。
- 第 2 个咒语:2 * [8,5,8] = [16,10,16] 。总共 2 个成功组合。
所以返回 [2,0,2] 。
提示:
n == spells.lengthm == potions.length1 <= n, m <= 1051 <= spells[i], potions[i] <= 1051 <= success <= 1010
解决方案:
算法目标
对于每个法术,统计有多少药水能够与之组成成功对(满足:法术强度 × 药水强度 ≥ 目标值)
核心思路
-
预处理药水:先排序药水数组,便于二分查找
-
对每个法术:找到能使其成功的最小药水强度
-
二分统计:通过二分查找统计满足条件的药水数量
关键步骤
1. 排序药水
sort(potions.begin(), potions.end());
-
排序后可以使用二分查找高效定位
-
时间复杂度:O(m log m),m为药水数量
2. 计算每个法术的成功对数
spells[i] = p_len - lower_bound(potions, 1.0*success/spells[i]);
-
1.0*success/spells[i]:计算所需的最小药水强度-
使用浮点数避免整数除法向下取整
-
需要满足:
spell × potion ≥ success -
即:
potion ≥ success / spell
-
-
lower_bound(potions, min_value):返回第一个 ≥ 最小药水强度的位置 -
p_len - 位置:从该位置开始的所有药水都满足条件
算法流程示例
输入:
spells = [5, 1, 3] potions = [1, 2, 3, 4, 5] success = 7
-
排序后 potions = [1, 2, 3, 4, 5]
-
计算:
-
法术5:
7/5.0=1.4→ 第一个≥1.4的药水是2(索引1)→ 成功对数=5-1=4 -
法术1:
7/1.0=7.0→ 第一个≥7.0的药水不存在(索引5)→ 成功对数=5-5=0 -
法术3:
7/3.0≈2.333→ 第一个≥2.333的药水是3(索引2)→ 成功对数=5-2=3
-
-
结果:[4, 0, 3]
时间复杂度
-
排序:O(m log m),m为药水数量
-
计算每个法术:n次二分查找,每次O(log m)
-
总时间:O(m log m + n log m)
潜在问题
-
除零错误 :当
spells[i]=0时,1.0*success/0会崩溃 -
浮点数精度:浮点数比较可能有精度误差
-
溢出风险 :
success是long long,但计算时转为double
优点
-
代码简洁
-
充分利用二分查找提高效率
-
思路清晰直观
可改进点
-
添加对
spell=0的特殊处理 -
考虑使用整数向上取整避免浮点数精度问题
-
可以使用乘法比较替代除法
总体来说,这是一个利用排序+二分查找高效解决组合计数问题的典型算法。
函数源码:
cppclass Solution { public: int lower_bound(vector<int>& nums, double target) { int len =nums.size(); int left=-1; int right=len; int mid; while(left+1<right){ mid=(left+right)/2; if((double)nums[mid]<target){ left=mid; }else{ right=mid; } } return right; } vector<int> successfulPairs(vector<int>& spells, vector<int>& potions, long long success) { sort(potions.begin(),potions.end()); int len=spells.size(); int p_len=potions.size(); for(int i=0;i<len;i++){ spells[i]= p_len - lower_bound(potions,1.0*success/spells[i]); } return spells; } };