LeetCode 春季赛
1. 采购方案
小力将 N 个零件的报价存于数组 nums
。小力预算为 target
,假定小力仅购买两个零件,要求购买零件的花费不超过预算,请问他有多少种采购方案。
注意:答案需要以 1e9 + 7 (1000000007)
为底取模,如:计算初始结果为:1000000008
,请返回 1
示例 1:
输入:
nums = [2,5,3,5], target = 6
输出:
1
解释:预算内仅能购买 nums[0] 与 nums[2]。
示例 2:
输入:
nums = [2,2,1,9], target = 10
输出:
4
解释:符合预算的采购方案如下:
nums[0] + nums[1] = 4
nums[0] + nums[2] = 3
nums[1] + nums[2] = 3
nums[2] + nums[3] = 10
提示:
2 <= nums.length <= 10^5
1 <= nums[i], target <= 10^5
思路:
1、看到题眼“两个零件花费不超过”,结合题意,可以想到转换成去求一个有序数组 n u m s nums nums中 [ i , j ] [i,j] [i,j]范围内的任意两个数 n u m s [ i ] 、 n u m s [ j ] nums[i]、nums[j] nums[i]、nums[j]都满足 n u m s [ i ] + n u m s [ j ] < = t a r g e t nums[i] + nums[j] <= target nums[i]+nums[j]<=target
2、基于这个可以用到二分法或者双指针,一个从头遍历,一个从尾部遍历,相当于可以夹着往中间求。
因为对于每一位元素都为非负数且从小到大升序的有序数组 n u m s nums nums而言任意的 i , j i,j i,j,任意元素 n u m s [ k ] nums[k] nums[k]。如果满足 i ≤ j i \leq j i≤j
则必有
n u m s [ i ] + n u m s [ k ] ≤ n u m s [ j ] + n u m s [ k ] nums[i] + nums[k] \leq nums[j] + nums[k] nums[i]+nums[k]≤nums[j]+nums[k]
因此假设 n u m s [ i ] + n u m s [ j ] > t a r g e t nums[i] + nums[j] > target nums[i]+nums[j]>target,那么如果 n u m s nums nums的下标为 [ i , j ] [i,j] [i,j]范围内存在另外一个数 n u m s [ k ] nums[k] nums[k] 使得 n u m s [ i ] + n u m s [ k ] ≤ t a r g e t nums[i] + nums[k] \leq target nums[i]+nums[k]≤target的话, k k k必定是在 [ i + 1 , j − 1 ] [i+1,j-1] [i+1,j−1]范围内。因此这样就可以逐渐缩小右边界,当右边界已经满足不了 n u m s [ i ] + n u m s [ j ] ≤ t a r g e t nums[i] + nums[j] \leq target nums[i]+nums[j]≤target的时候,可以继续考虑 i , j i,j i,j同时往中间走,否则对于任何只移动一端的话会更加满足不了这种条件。
整体代码如下:
public class Solution{
public int purchasePlans(int[] nums, int target) {
Arrays.sort(nums);
int r = nums.length - 1;
long res = 0;
int mod = (int) 1e9 + 7;
for (int l = 0; l < r; l++) {
while (r > l && nums[l] + nums[r] > target) {
r--;
}
res = (res + r - l) % mod;
}
return (int) res;
}
}
3. 魔塔游戏
小扣当前位于魔塔游戏第一层,共有 N
个房间,编号为 0 ~ N-1
。每个房间的补血道具/怪物对于血量影响记于数组 nums
,其中正数表示道具补血数值,即血量增加对应数值;负数表示怪物造成伤害值,即血量减少对应数值;0
表示房间对血量无影响。
小扣初始血量为 1,且无上限。假定小扣原计划按房间编号升序访问所有房间补血/打怪,为保证血量始终为正值,小扣需对房间访问顺序进行调整,每次仅能将一个怪物房间(负数的房间)调整至访问顺序末尾。请返回小扣最少需要调整几次,才能顺利访问所有房间。若调整顺序也无法访问完全部房间,请返回 -1。
示例 1:
输入:
nums = [100,100,100,-250,-60,-140,-50,-50,100,150]
输出:
1
解释:初始血量为 1。至少需要将 nums[3] 调整至访问顺序末尾以满足要求。
示例 2:
输入:
nums = [-200,-300,400,0]
输出:
-1
解释:调整访问顺序也无法完成全部房间的访问。
提示:
1 <= nums.length <= 10^5
-10^5 <= nums[i] <= 10^5
思考:
这个题可以考虑用贪心的做法来做:
对于每一个怪物房间,可以考虑首先将当前使得血量总值不满足正数的且绝对值最大的放到最后,这样子调整的次数会是最少的,证明如下:
对于任意的怪物 n u m s [ i ] , n u m s [ j ] nums[i],nums[j] nums[i],nums[j],假设满足 n u m s [ i ] nums[i] nums[i]是负数,满足 n u m s [ i ] > s u m nums[i] > sum nums[i]>sum即此时血量不为正数了,并且 n u m s [ i ] nums[i] nums[i]是绝对值最大的一个,假设此时将 n u m s [ i ] nums[i] nums[i]调整到最后的次数使得满足血量为正的操作次数为 x x x,或者不调整 n u m s [ i ] nums[i] nums[i]而是将 n u m s [ j ] nums[j] nums[j]调整到最后使得满足血量为正的操作次数为,此时必然满足 x < y x < y x<y
考虑到了需要用优先队列来维护怪物造成伤害值的值,因此需要借助PriorityQueue,注意是从大到小排序,存进去的顺序是怪物造成伤害值的绝对值。
用 r e s res res来记录调整的次数, l a s t last last变量来维护每次调整到最后的一个怪物伤害值大小的绝对值的和,来与前面所有调整后血量还不为0的血量总和 s u m sum sum来进行比较,如果调整后的所有伤害值大小 l a s t last last比一直增加的血量和 s u m sum sum要大,则说明无论如何调整都不可能满足题目条件,否则输出 r e s res res
class Solution {
public int magicTower(int[] nums) {
int len = nums.length;
long last = 0;
long sum = 1;
int res = 0;
PriorityQueue<Integer> q = new PriorityQueue<>((x,y) -> y - x);
for (int i = 0; i < len; i++) {
if (nums[i] >= 0) {
sum += nums[i];
} else {
q.add(-nums[i]);
if (-nums[i] > sum) {
int poll = q.poll();
res++;
sum += poll;
last += poll;
}
sum += nums[i];
}
}
if (sum <= last) {
return -1;
}
return res;
}
}