递归 拿走最大分数牌

119 阅读2分钟

题目

给定一个数组,每个元素代表卡牌分数,规定每次A先拿,然后B拿,每次拿牌只能拿最左或最右的一张牌,玩家A、B都绝顶聪明,返回最后获胜者的分数(最大分数); 举例:[1,2,100,4],开始时A只能拿1或4,玩家A作为绝顶聪明的人绝不会拿4,因为拿4后B可以拿到100,所以玩家A会先拿1,然后B拿4,然后A拿100,然后B拿2结束游戏

  • 因为并不能预先知道A拿哪边是最优解,所以需要构造拿左边和拿右边的两条路,最终通过Math.max来获得A最大分数
  • 当A拿了一次后,因为B绝顶聪明,所以留给A的一定是最小的,所以第二次选择时,需要根据可选区间,选择最小的那条路,并且当第二次选择只有一个数时,需要B选,A不能再选了,返回0,当选择时只剩一个数,直接返回该数
function winner(arr) {
  // 在选择时,选择可选区间最大的那个
  function choose(arr, i, j) {
    // 因为是先选,当区间只剩一个数时,就只能选择该数
    if (i === j) {
      return arr[i];
    }
    // 因为先手选了,所以在剩余区间要别人先选再后手选,所以secondChoose表示在剩余区间后手选的分数
    return Math.max(
      arr[i] + secondChoose(arr, i + 1, j),
      arr[j] + secondChoose(arr, i, j - 1)
    );
  }
  
  // secondChoose实际上还是递归调用choose进行选择
  function secondChoose(arr, i, j) {
    // 因为是后手选,当区间只剩一个数时,返回0
    if (i === j) {
      return 0;
    }
    // 因为是后手选,所以留下的一定是最小的那个,只能选最小的那个
    // i+1表示先手选了i位置,j-1表示先手选了j位置
    return Math.min(choose(arr, i + 1, j), choose(arr, i, j - 1));
  }

  return Math.max(
    choose(arr, 0, arr.length - 1),  //先选玩家
    secondChoose(arr, 0, arr.length - 1)//后选玩家
  );
  // return choose(arr,0,arr.length-1)
  // return secondChoose(arr,0,arr.length-1)
}
//[1,2,100,4]
//[1,100,2]
console.log(winner([1, 100, 2]));