You have an initial power P
, an initial score of 0
points, and a bag of tokens.
Each token can be used at most once, has a value token[i]
, and has potentially two ways to use it.
- If we have at least
token[i]
power, we may play the token face up, losingtoken[i]
power, and gaining1
point. - If we have at least
1
point, we may play the token face down, gainingtoken[i]
power, and losing1
point.
Return the largest number of points we can have after playing any number of tokens.
Example 1:
Input: tokens = [100], P = 50
Output: 0
Example 2:
Input: tokens = [100,200], P = 150
Output: 1
Example 3:
Input: tokens = [100,200,300,400], P = 200
Output: 2
Note:
tokens.length <= 1000
0 <= tokens[i] < 10000
0 <= P < 10000
這道題說是給了一個初始力量值P,然后有一個 tokens 數組,有兩種操作可以選擇,一種是減去 tokens[i] 的力量,得到一分,但是前提是減去后剩余的力量不能為負。另一種是減去一分,得到 tokens[i] 的力量,前提是減去后的分數不能為負,問一頓操作猛如虎后可以得到的最高分數是多少。這道題其實題意不是太容易理解,而且例子也沒給解釋,博主也是讀了好幾遍題目才明白的。比如例子3,開始有 200 的力量,可以先花 100,得到1個積分,此時還剩 100 的力量,但由於剩下的 token 值都太大,沒法換積分了,只能用積分來換力量,既然都是花一個1個積分,肯定是要換最多的力量,於是換來 400 力量,此時總共有 500 的力量,積分還是0,但是一頓操作后,白嫖了 400 的力量,豈不美哉?!這 500 的力量剛好可以換兩個積分,所以最后返回的就是2。通過上述分析,基本上可以知道策略了,從最小的 token 開始,用力量換積分,當力量不夠時,就用基本換最大的力量,如果沒有積分可以換力量,就結束,或者所有的 token 都使用過了,也結束,這就是典型的貪婪算法 Greedy Algorithm,也算對得起其 Medium 的身價。這里先給 tokens 數組排個序,然后使用雙指針i和j,分別指向開頭和末尾,當 i<=j 進行循環,從小的 token 開始查找,只要力量夠,就換成積分,不能換的時候,假如 i>j 或者此時積分為0,則退出;否則用一個積分換最大的力量,參見代碼如下:
解法一:
class Solution {
public:
int bagOfTokensScore(vector<int>& tokens, int P) {
int res = 0, cur = 0, n = tokens.size(), i = 0, j = n - 1;
sort(tokens.begin(), tokens.end());
while (i <= j) {
while (i <= j && tokens[i] <= P) {
P -= tokens[i++];
res = max(res, ++cur);
}
if (i > j || cur == 0) break;
--cur;
P += tokens[j--];
}
return res;
}
};
我們也可以換一種寫法,不用 while 套 while,而是換成賞心悅目的 if ... else 語句,其實也沒差啦,參見代碼如下:
解法二:
class Solution {
public:
int bagOfTokensScore(vector<int>& tokens, int P) {
int res = 0, cur = 0, n = tokens.size(), i = 0, j = n - 1;
sort(tokens.begin(), tokens.end());
while (i <= j) {
if (P >= tokens[i]) {
P -= tokens[i++];
res = max(res, ++cur);
} else if (cur > 0) {
--cur;
P += tokens[j--];
} else {
break;
}
}
return res;
}
};
我們也可以使用遞歸來做,使用一個子函數 helper,將i和j當作參數輸入,其實原理跟上的方法一摸一樣,不難理解,參見代碼如下:
解法三:
class Solution {
public:
int bagOfTokensScore(vector<int>& tokens, int P) {
sort(tokens.begin(), tokens.end());
return helper(tokens, P, 0, (int)tokens.size() - 1, 0);
}
int helper(vector<int>& tokens, int P, int i, int j, int cur) {
if (i > j) return cur;
int res = cur;
if (tokens[i] <= P) {
res = max(res, helper(tokens, P - tokens[i], i + 1, j, cur + 1));
} else if (cur > 0) {
res = max(res, helper(tokens, P + tokens[j], i, j - 1, cur - 1));
}
return res;
}
};
Github 同步地址:
https://github.com/grandyang/leetcode/issues/948
參考資料:
https://leetcode.com/problems/bag-of-tokens/
https://leetcode.com/problems/bag-of-tokens/discuss/383249/Java-Solution-With-Explanation
https://leetcode.com/problems/bag-of-tokens/discuss/197696/C%2B%2BJavaPython-Greedy-%2B-Two-Pointers