采购方案 | 刷题打卡

228 阅读1分钟

本文正在参与掘金团队号上线活动,点击 查看大厂春招职位

一、题目描述:

image.png

image.png

二、思路分析:

今天给大家分享的是力扣春季杯个人赛的第一题,可惜当时报名了没参加,现在来回顾一下看看。第一印象一看,咦,这不是找一个长度为2的子集,要是其和满足小于等于target就是一个满足条件的吗?哈哈哈哈,我在这里没尝试,不过感觉可能会超时?[狗头]

二分

定睛一看,看到题目的数据量10^5,应该往NlogN复杂度上靠。一个for循环,里面一个二分,正好吗这不是。

  • 首先对原数组排序,然后开始依次遍历所有元素,要是当前元素已经大于等于target了,直接结束遍历。否则,进入二分查找有多少元素满足和当前元素加起来和小于等于target的数量。

  • 二分,传入的是left,right边界,然后查找有多少元素小于等于target,注意,这里的target不是题目要找的target。找到满足条件的最右边界后,直接返回即可。

  • 时间复杂度:O(NlogN),这里 N 是数组的长度。

  • 空间复杂度:O(1)

三、AC 代码:

class Solution {

    /**
     * @param Integer[] $nums
     * @param Integer $target
     * @return Integer
     */
    function purchasePlans($nums, $target) {
        $len_n = count($nums);
        sort($nums);
        $ret = 0;
        for ($i = 0; $i < $len_n - 1; $i++) {
            if ($nums[$i] >= $target) {
                break;
            }
            $t = $this->find($nums, $i + 1, $len_n - 1, $target - $nums[$i]);
            echo "nums{$nums[$i]}, t:$t".PHP_EOL;
            $ret += $t;
        }
        return $ret % (1000000007);
    }

    protected function find(&$nums, $left, $right, $target) {
        $start = $left;
        if ($nums[$left] > $target) {
            return 0;
        }
        while ($left < $right) {
            $mid = $left + (int)(($right - $left + 1) / 2);
            if ($nums[$mid] > $target) {
                $right = $mid - 1;
            } else {
                $left = $mid;
            }
        }

//循环结束是left == right,left 是最后一个满足要求的元素的位置,所以返回即可
        return $left - $start + 1;
    }
}

四、总结:

有时候根据题目提供的数据量选择合适的算法也是一个小技巧吧。