题目描述
给你一个下标从 0 开始的正整数数组 candiesCount ,其中 candiesCount[i] 表示你拥有的第 i 类糖果的数目。同时给你一个二维数组 queries ,其中 queries[i] = [favoriteTypei, favoriteDayi, dailyCapi] 。
你按照如下规则进行一场游戏:
-
你从第 0 天开始吃糖果。
-
你在吃完所有第 i - 1 类糖果之前,不能吃任何一颗第 i 类糖果。
-
在吃完所有糖果之前,你必须每天至少吃一颗糖果。
请你构建一个布尔型数组 answer ,满足 answer.length == queries.length 。answer[i] 为 true 的条件是:在每天吃不超过 dailyCapi 颗糖果的前提下,你可以在第 favoriteDayi 天吃到第 favoriteTypei 类糖果;否则 answer[i] 为 false 。注意,只要满足上面 3 条规则中的第二条规则,你就可以在同一天吃不同类型的糖果。
请你返回得到的数组 answer 。
示例 1:
输入:candiesCount = [7,4,5,3,8], queries = [[0,2,2],[4,2,4],[2,13,1000000000]]
输出:[true,false,true]
提示:
1- 在第 0 天吃 2 颗糖果(类型 0),第 1 天吃 2 颗糖果(类型 0),第 2 天你可以吃到类型 0 的糖果。
2 - 每天你最多吃 4 颗糖果。即使第 0 天吃 4 颗糖果(类型 0),第 1 天吃 4 颗糖果(类型 0 和类型 1),你也没办法在第 2 天吃到类型 4 的糖果。换言之,你没法在每天吃 4 颗糖果的限制下在第 2 天吃到第 4 类糖果。
3 - 如果你每天吃 1 颗糖果,你可以在第 13 天吃到类型 2 的糖果。
示例 2:
输入:candiesCount = [5,2,6,4,1], queries = [[3,1,2],[4,10,3],[3,10,100],[4,100,30],[1,3,1]]
输出:[false,true,true,false,false]
提示:
-
1 <= candiesCount.length <= 10^5
-
1 <= candiesCount[i] <= 10^5
-
1 <= queries.length <= 10^5
-
queries[i].length == 3
-
0 <= favoriteTypei < candiesCount.length
-
0 <= favoriteDayi <= 10^9
-
1 <= dailyCapi <= 10^9
题目链接:https://leetcode-cn.com/problems/can-you-eat-your-favorite-candy-on-your-favorite-day/
题目描述就是上面那样,有点长,也是看了几遍才看懂题意,多看两遍就懂了。老哥们把我想说的话都说了↓↓↓↓↓↓
解题思路
就一句话:算出想吃的糖最少和最多分别需要多少天能吃到,然后判断 favoriteDay 是不是在这俩数的范围内。
好的就是这样。
但是真正写起代码来,还是废了不少时间的↓↓↓↓↓
这这这这这么长一串提交记录......
按我写的版本一版一版来,看看最后是如何 AC 的。
ver 1.0
class Solution {
public boolean[] canEat(int[] candiesCount, int[][] queries) {
boolean[] res = new boolean[queries.length];
for (int i = 0 ; i < queries.length ; i++){
res[i] = canEat(candiesCount, queries[i]);
}
return res;
}
private boolean canEat(int[] candiesCount, int[] query) {
int dailyCap = query[2]; // 每天最多吃的糖果数
int favoriteDay = query[1]; // 想要在第 这么多天 吃到favoriteType类型的糖果
int favoriteType = query[0]; // 想要吃的糖果类型
int min = 0;
for (int i = 0 ; i < favoriteType ; i++){
min += candiesCount[i];
}
min = min/dailyCap;
int max = 0;
for(int i = 0 ; i <= favoriteType; i++) {
max+=candiesCount[i];
}
return favoriteDay >= min && favoriteDay < max;
}
}
这是我最开始的解法,代码很好懂,但是超时。
我注意到,boolean canEat 函数中的两个 for 循环实际上是不必要的,可以将 min、max 放在同一个 for 循环中,因为它俩之间就差一个 candiesCount[favoriteType],于是有了下面的代码。
于是我继续改。
ver 2.0
class Solution {
public boolean[] canEat(int[] candiesCount, int[][] queries) {
boolean[] res = new boolean[queries.length];
for (int i = 0 ; i < queries.length ; i++){
res[i] = canEat(candiesCount, queries[i]);
}
return res;
}
private boolean canEat(int[] candiesCount, int[] query) {
int dailyCap = query[2]; // 每天最多吃的糖果数
int favoriteDay = query[1]; // 想要在第 这么多天 吃到favoriteType类型的糖果
int favoriteType = query[0]; // 想要吃的糖果类型
int max = 0;
for (int i = 0 ; i <= favoriteType ; i++){
max += candiesCount[i];
}
return favoriteDay >= (max-candiesCount[favoriteType])/dailyCap && favoriteDay < max;
}
}
直接将 min 去掉了,max - candiesCount[favoriteType] 就是 min。
但还是超时。
然后我就又陷入了思考,因为这个解法是 O(n^2) 的复杂度,既然 n^2 都超时了,那么自然是想着看看能不能优化到 O(n) 的复杂度去。答案是可以的。
注意到,上面实际上是有不必要的重复操作的,就是针对每一个 query,都会去加一遍 max,都是从 0 开始加,这自然是一种不必要的操作,于是就有了下面的解法↓↓↓↓↓↓
ver 3.0
class Solution {
public boolean[] canEat(int[] candiesCount, int[][] queries) {
boolean[] res = new boolean[queries.length];
long[] prefixSum = new long[candiesCount.length];
prefixSum[0] = candiesCount[0];
for(int i = 1 ; i < candiesCount.length ; i++){
prefixSum[i] = prefixSum[i-1] + candiesCount[i];
}
for (int i = 0 ; i < queries.length ; i++){
int dailyCap = queries[i][2]; // 每天最多吃的糖果数
int favoriteDay = queries[i][1]; // 想要在第 这么多天 吃到favoriteType类型的糖果
int favoriteType = queries[i][0]; // 想要吃的糖果类型
res[i] = favoriteDay >= (prefixSum[favoriteType] - candiesCount[favoriteType])/dailyCap && favoriteDay < prefixSum[favoriteType];
}
return res;
}
}
加一个 prefixSum 数组,prefixSum[i] 表示的是 candiesCount 数组前 i 个和。这样就可以在 O(1) 的时间复杂度得到 ver1.0 中的 min 和 max 了。进而将算法优化到 O(n) 的复杂度,自然也是顺利通过啦!
就是这个效率嘛...... 有点不敢恭维......
管他呢,做出来就行!
有一个需要注意的点是:prefixSum 数组的值会超过 int 表示范围,所以这里用 long 型。