There are n coins with different value in a line. Two players take turns to take one or two coins from left side until there are no more coins left. The player who take the coins with the most value wins.
Could you please decide the first player will win or lose?
Given values array A = [1,2,2]
, return true
.
Given A = [1,2,4]
, return false
.
這道題是之前那道Coins in a Line的延伸,由於每個硬幣的面值不同,所以那道題的數學解法就不行了,這里我們需要使用一種方法叫做極小化極大算法Minimax,這是博弈論中比較經典的一種思想,LeetCode上有一道需要用這種思路解的題Guess Number Higher or Lower II。這道題如果沒有接觸過相類似的題,感覺還是蠻有難度的。我們需要用DP來解,我們定義一個一維數組dp,其中dp[i]表示從i到end可取的最大錢數,大小比values數組多出一位,若n為values的長度,那么dp[n]先初始化為0。我們是從后往前推,我們想如果是values數組的最后一位,及i = n-1時,此時dp[n-1]應該初始化為values[n-1],因為拿了肯定比不拿大,錢又沒有負面額;那么繼續往前推,當i=n-2時,dp[n-2]應該初始化為values[n-2]+values[n-1],應為最多可以拿兩個,所以最大值肯定是兩個都拿;當i=n-3時,dp[n-3]應該初始化為values[n-3]+values[n-2],因為此時還剩三個硬幣,你若只拿一個,那么就會給對手留兩個,當然不行,所以自己要拿兩個,只能給對手留一個,那么到目前位置初始化的步驟就完成了,下面就需要找遞推式了:
當我們處在i處時,我們有兩種選擇,拿一個還是拿兩個硬幣,我們現在分情況討論:
1. 當我們只拿一個硬幣values[i]時,那么對手有兩種選擇,拿一個硬幣values[i+1],或者拿兩個硬幣values[i+1] + values[i+2]
a) 當對手只拿一個硬幣values[i+1]時,我們只能從i+2到end之間來取硬幣,所以我們能拿到的最大硬幣數為dp[i+2]
b) 當對手拿兩個硬幣values[i+1] + values[i+2]時,我們只能從i+3到end之間來取硬幣,所以我們能拿到的最大硬幣數為dp[i+3]
由於對手的目的是讓我們拿較小的硬幣,所以我們只能拿dp[i+2]和dp[i+3]中較小的一個,所以對於我們只拿一個硬幣的情況,我們能拿到的最大錢數為values[i] + min(dp[i+2], dp[i+3])
2. 當我們拿兩個硬幣values[i] + values[i + 1]時,那么對手有兩種選擇,拿一個硬幣values[i+2],或者拿兩個硬幣values[i+2] + values[i+3]
a) 當對手只拿一個硬幣values[i+2]時,我們只能從i+3到end之間來取硬幣,所以我們能拿到的最大硬幣數為dp[i+3]
b) 當對手拿兩個硬幣values[i+2] + values[i+3]時,我們只能從i+4到end之間來取硬幣,所以我們能拿到的最大硬幣數為dp[i+4]
由於對手的目的是讓我們拿較小的硬幣,所以我們只能拿dp[i+3]和dp[i+4]中較小的一個,所以對於我們只拿一個硬幣的情況,我們能拿到的最大錢數為values[i] + values[i + 1] + min(dp[i+3], dp[i+4])
綜上所述,遞推式就有了 dp[i] = max(values[i] + min(dp[i+2], dp[i+3]), values[i] + values[i + 1] + min(dp[i+3], dp[i+4]))
這樣當我們算出了dp[0],知道了第一個玩家能取出的最大錢數,我們只需要算出總錢數,然后就能計算出另一個玩家能取出的錢數,二者比較就知道第一個玩家能否贏了,參見代碼如下:
class Solution { public: /** * @param values: a vector of integers * @return: a boolean which equals to true if the first player will win */ bool firstWillWin(vector<int> &values) { if (values.size() <= 2) return true; int n = values.size(), sum = 0; vector<int> dp(n + 1, 0); dp[n - 1] = values[n - 1]; dp[n - 2] = values[n - 2] + values[n - 1]; dp[n - 3] = values[n - 3] + values[n - 2]; for (int i = n - 4; i >= 0; --i) { dp[i] = max(values[i] + min(dp[i + 2], dp[i + 3]), values[i] + values[i + 1] + min(dp[i + 3], dp[i + 4])); } for (int d : values) { sum += d; } return sum - dp[0] < dp[0]; } };
類似題目:
Guess Number Higher or Lower II
參考資料: