从递归开始入手区间DP

博客园,启动
题目:预测赢家
题目链接:
https://leetcode.cn/problems/predict-the-winner/description/
题面:
给你一个整数数组 nums 。玩家 1 和玩家 2 基于这个数组设计了一个游戏。
玩家 1 和玩家 2 轮流进行自己的回合,玩家 1 先手。开始时,两个玩家的初始分值都是 0 。每一回合,玩家从数组的任意一端取一个数字(即,nums[0] 或 nums[nums.length - 1]),取到的数字将会从数组中移除(数组长度减 1 )。玩家选中的数字将会加到他的得分上。当数组中没有剩余数字可取时,游戏结束。
如果玩家 1 能成为赢家,返回 true 。如果两个玩家得分相等,同样认为玩家 1 是游戏的赢家,也返回 true 。你可以假设每个玩家的玩法都会使他的分数最大化。

这道题是很经典的从两端进行可能性展开的区间DP,下面会从暴力递归-记忆化搜索-区间DP逐步对这道题进行分析。

刚拿到这道题以为是贪心,但是很容易就能明白贪心都会有很明显的错误
如样例:10 100 40 2,很明显最优解是拿走2和100。无论怎么贪心都是实现不了的

暴力递归

分析题目,我们可以发现题干有很明显的零和博弈,我们在设计递归时要兼顾玩家1和玩家2的博弈行为

设计递归函数
int dfs(int left,int right);
意为进行该玩家1进行操作时还有[left,right]范围的数还没有被选择。

在进行递归时,会出现如下几种情况

  1. 当left==right时,就剩下一个数没选,所以玩家1会毫不留情的拿走。
  2. 当left+1==right时,剩下两个数没选,所以玩家1会将两个数中的最大值拿走。
  3. 一般情况。

我们现在对一般情况进行讨论。
如果玩家1拿走了left位置上的数的话,那么玩家1下一次选择的可能会有

  1. 玩家2拿走了left+1位置的元素,玩家1从left+2与right位置上的数中选一个
  2. 玩家2拿走了high位置上的元素,玩家1从left+1与right-1位置上的数选一个。

当玩家2拿走right位置上的元素的情况就不过多赘述。

那我们如何实现玩家1和玩家2之间的博弈操作?
试想以下,如果玩家2也想取得最大值的话是不是等同于让玩家1尽可能取得最小值。
所以当玩家1在选择某个数后玩家2让其获得玩家1下一次进行的两次操作的最小值就能实现博弈操作。

代码如下

点击查看代码
int dfs(int low, int high)
{
    if (low == high)
        return arr[low];
    if (low + 1 == high)
        return max(arr[low], arr[high]);
    int p1 = min(dfs(low + 1, high - 1) + arr[low], dfs(low + 2, high) + arr[low]);
    int p2 = min(dfs(low + 1, high - 1) + arr[high], dfs(low, high - 2) + arr[high]);
    return max(p1, p2);
}

记忆化搜索?挂个缓存表就行,不过多赘述。
准备了那么多,接下来就是如何将递归转化为动态规划。

  1. 首先是根据递归初始化数组,参考上面第一和第二种情况。
  2. 搞清楚数组中某一位置对其他位置的依赖关系,从而决定循环进行的方向。

就以这道题为例
我们在得到某一位置(用A来表示)的答案之前就要得到对应位置的答案(用B来表示)

B A
B
B

位置依赖根据写的具体的递归操作来决定

以上就是我总结出来的一些经验,如果有错误的地方默认博主是错的就行

posted @ 2025-08-08 20:54  zznu_ybk  阅读(8)  评论(0)    收藏  举报