leetcode 486. Predict the Winner (Alpha-Beta剪枝实现关键点小总结)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/luke2834/article/details/81807569

题意

  • 小的博弈游戏,两个人轮流从一个数组的两端取数,直到取完,最后取的和最大的人获胜。问先手能否赢?其中如果和相同,先手胜。

思路

  • 首先如果有偶数个元素,先手必胜,这个可以参考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;
    }
};

猜你喜欢

转载自blog.csdn.net/luke2834/article/details/81807569