1. 题目来源
链接:LCP 28. 采购方案
2. 题目解析
简单问题,签到题。
排序后双指针很明显,不用多说。
二分的话比较恶心,首先,二分区间需要明确,是 [ i + 1 , n − 1 ] [i+1,n-1] [i+1,n−1] 这个区间,不能自己买自己。要满足 a + b ≤ t a r g e t a+b\le target a+b≤target,对于每一个 a a a,找到最大的一个 b b b 即可,在区间 [ i + 1 , n − 1 ] [i+1,n-1] [i+1,n−1] 中寻找,找到最大的且满足 a + b ≤ t a r g e t a+b\le target a+b≤target。但是要注意一点, b b b 可能就没有合法的存在,仍会停在 i + 1 i+1 i+1 这个位置,也会产生一种方案。所以,需要在最后累加方案的时候判断一下,当前位置的 b b b,是否是合法的,即判断 a + b ≤ t a r g e t a+b\le target a+b≤target 是否成立。
- 时间复杂度: O ( n l o g n ) O(nlogn) O(nlogn)。
- 空间复杂度: O ( 1 ) O(1) O(1)
代码:
二分:
class Solution {
public:
int purchasePlans(vector<int>& nums, int target) {
const int MOD = 1e9 + 7;
sort(nums.begin(), nums.end());
long long res = 0;
for (int i = 0; i < nums.size(); i ++ ) {
int l = i + 1, r = nums.size() - 1;
while (l < r) {
int mid = l + r + 1 >> 1;
if (nums[i] + nums[mid] > target) r = mid - 1;
else l = mid;
}
if (nums[r] + nums[i] <= target) res += (r - i);
}
return res % MOD;
}
};
双指针:
对于左指针 i i i 向右移动变大的过程中,右指针 j j j 不会再向右移动,只会向左移动。故满足双指针的性质。相当于一个窗口,慢慢变小,变小。
class Solution {
public:
int purchasePlans(vector<int>& nums, int target) {
const int MOD = 1e9 + 7;
sort(nums.begin(), nums.end());
long long res = 0;
for (int i = 0, j = nums.size() - 1; i < nums.size(); i ++ ) {
while (j > i && nums[i] + nums[j] > target) j -- ;
if (j > i) res += j - i;
}
return res % MOD;
}
};