leetcode 486. Predict the Winner (Alpha-Beta剪枝實現關鍵點小總結)
阿新 • • 發佈:2018-11-11
題意
- 小的博弈遊戲,兩個人輪流從一個數組的兩端取數,直到取完,最後取的和最大的人獲勝。問先手能否贏?其中如果和相同,先手勝。
思路
- 首先如果有偶數個元素,先手必勝,這個可以參考leetcode 877題求解思路,證明連結
- 奇數個的時候就沒有這麼好的結論了,博弈dp是很容易做的方法,這裡就不討論dp了,我們嘗試用alpha-beta剪枝的博弈樹來解決。
- 博弈樹很簡單,其實就是個dfs,比如我要走下一步了,那從我當前步開始搜尋,其實就在構建一顆搜尋樹。設根為第0層,那麼搜尋樹裡偶數層對應的就是我要走的狀態,奇數層對應對手要走的狀態,所以偶數層的目標是從孩子裡選一個局面分數對我來說最大的,奇數層則是從孩子裡選一個局面分數對我來說最小的。
- alpha-beta剪枝:核心思想就是,alpha是當前的下界,beta是當前的上界,設當前節點,在遍歷了一部分孩子後,他當前的分數是N,應該滿足alpha <= N <= beta
- 詳細的解釋參見:詳細解釋
- 這裡給一下個人覺得的實現時需要記住的關鍵點:
- 偶數層節點,只會修改下界alpha,奇數層節點只修改上界beta
- 判斷alpha和beta,如果不滿足條件
alpha < beta
,則不用繼續向下搜尋了(這個判斷是在每遞迴遍歷完一個孩子之後都要判斷的) - 除了上下界,當前節點算出的結果是同樣是要儲存的,最後返回的是當前節點的計算結果
實現
class Solution {
public:
int INF = 2e8+5;
bool PredictTheWinner(vector<int>& nums) {
if (!(nums.size() & 1))
return true;
int ret = FindMax(nums, 0, nums.size() - 1, -INF, INF, 0);
return ret >= 0;
}
int FindMax(vector<int >& nums, int st, int ed, int alpha, int beta, int pre){
if (st == ed){
return pre + nums[st];
}
int ret = FindMin(nums, st + 1, ed, alpha, beta, pre + nums[st]);
alpha = max(alpha, ret);
if (alpha >= beta)
return alpha;
ret = max(ret, FindMin(nums, st, ed - 1, alpha, beta, pre + nums[ed]));
return ret;
}
int FindMin(vector<int>& nums, int st, int ed, int alpha, int beta, int pre){
if (st == ed){
return pre - nums[st];
}
int ret = FindMax(nums, st + 1, ed, alpha, beta, pre - nums[st]);
beta = min(beta, ret);
if (alpha >= beta)
return beta;
ret = min(ret, FindMax(nums, st, ed - 1, alpha, beta, pre - nums[ed]));
return ret;
}
};