每日leetcode

2300. 咒语和药水的成功对数 - 力扣(LeetCode)

题目

给你两个正整数数组 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.length

  • m == potions.length

  • 1 <= n, m <= 105

  • 1 <= spells[i], potions[i] <= 105

  • 1 <= success <= 1010

思路

  1. 因为返回的元素下标是要和咒语对应的,所以咒语数组不能改变,但是为了加速搜索速度,我们可以排序药水数组,然后通过二分找到最小的满足的药水,其后的药水都能满足,那么我们就得到了该咒语匹配的药水数量了。
  2. 循环n次匹配n个咒语之后即可。
  3. 这里二分查找的判断目标是可以固定的,通过success/spells[i] + ((sucecess%spells[i]==0)?0:1)来确定最小的potion的值。

代码实现

class Solution {
public:
    vector<int> successfulPairs(vector<int>& spells, vector<int>& potions, long long success) {
        vector<int> ans;
        int pn = potions.size();
        sort(potions.begin(), potions.end());
        for(int i = 0; i < spells.size(); ++i) {
            int left = 0, right = pn;
            long long effect = success/spells[i] + ((success%spells[i]==0)?0:1);
            while(left < right) {
                int mid = left + (right-left) / 2;
                if(potions[mid] >= effect) right = mid;
                else left = mid + 1;
            }
            ans.emplace_back(pn-right);
        }
        return ans;
    }
};

复杂度分析

  • 时间复杂度:排序的时间复杂度是O(mlogm)的,遍历咒语数组的时间复杂度是O(n)的,内部二分查找的时间复杂度是O(logn)的,所以总的时间复杂度为O((m+n)logm)。
  • 空间复杂度:O(n)/O(1)。

题解

  • 官解的思路基本跟我差不多,另一种双指针的方法在同样时间复杂度下空间开销更大了,就不看了。
  • 相对的,另一个大佬的解法就成功实现了空间换时间,将复杂度降到了O(n),学习一下:
    • 首先,遍历potions数组找到其中的最大值,然后根据这个最大值建桶(如果空间复杂度有限制的话这个方法可能就不行了),然后在遍历一遍数组将元素填入桶内,最后再将每个桶的可行解个数倒序叠加。
    • 那么,遍历咒语数组的过程中,就只用确定是不是小于等于最大的potion值,如果是,直接去对应的桶里面提个数,否则为0。
  • 这样就实现了时间复杂度为O(n+m),空间复杂度为O(maxPotions)的代码了。
  • 复现:
  • class Solution {
    public:
        vector<int> successfulPairs(vector<int>& spells, vector<int>& potions, long long success) {
            int m = potions.size(), maxValue = 0, n = spells.size();
            vector<int> ans(n);
            for(int i = 0; i < m; ++i) maxValue = max(maxValue, potions[i]);
            vector<int> cnt(maxValue+2);
            for(int i = 0; i < m; ++i) cnt[potions[i]]++;
            for(int i = maxValue; i >= 1; --i) cnt[i] += cnt[i+1];
            for(int i = 0; i < n; ++i) {
                long long effect = success/spells[i] + (success%spells[i]==0?0:1);
                if(effect <= maxValue) ans[i] = cnt[effect];
            }
            return ans;
        }
    };

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值