LeetCode 1140. Stone Game II

题目

LeetCode 1140. Stone Game II_第1张图片

思路

标签:

  • 动态规划

定义:

  • stones:与题目中 piles 意思保持一致;
  • n:stone个数;
  • score[i, j]:当前玩家从 stones[i] 开始,且 M = j 时,该玩家所能得到的最高分数;
  • sum[i]:从stones[i]开始,剩下石头分数之和(即,stones[i] + … + stones[n - 1]);

考虑:

  • 如何使得当前玩家所能获得的分数score[i, j]最高?取1个?取2个?… 取 2 * j 个?
    • 当前玩家拿1个stone时,另一个玩家接下来所能获得的最高分数为score[i + 1, max(j, 1)];
    • 当前玩家拿2个stone时,另一个玩家接下来所能获得的最高分数为score[i + 2, max(j, 2)];
    • 当前玩家拿2*j个stone时,另一个玩家接下来所能获得的最高分数为score[i + 2 * j, max(j, 2 * j)];
  • 因为sum[i]是一定的,只需要使另一个玩家接下来所能获得的最高分数最小,即可保证当前玩家所得到的分数score[i, j]最大;

基于上述考虑,得到递归方程如下:
s c o r e [ i , j ] = s u m [ i ] − m i n ( s o c r e [ i + 1 , m a x ( j , 1 ) ] , . . . , s c o r e [ i + 2 ∗ j , m a x ( j , 2 ∗ j ) ] ) score[i, j] = sum[i] - min(socre[i + 1, max(j, 1)], ..., score[i + 2 * j, max(j, 2 * j)]) score[i,j]=sum[i]min(socre[i+1,max(j,1)],...,score[i+2j,max(j,2j)])

进一步考虑,递归终止条件:

  • 因为所有stone的分数都为正数;当 n - i <= 2 * j 时(即,当前玩家可以直接拿下剩下的所有stone),则当前玩家所得最高分 score[i, j] 直接为剩下所有stone分数之和(即,score[i, j] = sum[i]);

完整递归方程如下:
s c o r e [ i , j ] = { s u m [ i ] n − i < = 2 ∗ j T n − i > 2 ∗ j score[i, j] = \begin{cases} sum[i] & n - i <= 2 * j \\ T &n - i > 2 * j \end{cases} score[i,j]={sum[i]Tni<=2jni>2j
其 中 , T = s u m [ i ] − m i n ( s o c r e [ i + 1 , m a x ( j , 1 ) ] , . . . , s c o r e [ i + 2 ∗ j , m a x ( j , 2 ∗ j ) ] ) 其中,T = sum[i] - min(socre[i + 1, max(j, 1)], ..., score[i + 2 * j, max(j, 2 * j)]) T=sum[i]min(socre[i+1,max(j,1)],...,score[i+2j,max(j,2j)])

回到题目

  • 一场游戏,Alex所能获得的最高分为 s c o r e [ 0 , 1 ] score[0, 1] score[0,1]
  • 对应的,Lee所获得的分数为 s u m [ 0 ] − s c o r e [ 0 , 1 ] sum[0] - score[0, 1] sum[0]score[0,1]
  • 谁获得的分数高,谁赢;

实现思路

后缀和数组(suffix sum array)

  • 为了可以在常数时间内计算到sum[i],我们需要提前计算好后缀和数组;

当n = 8时,示例如下:

  • 正如前面所说,当 2 * j > n - i 时,当前玩家可以拿走剩下的所有stone,因此这里 j 的上限,我们取 n 的一半向上取整;
  • 为了计算score[0, 1],我们需要知道score[1, 1],score[2, 2],所以计算时我们采用从下到上(从左到右还是从右到左都行)的计算顺序;
    LeetCode 1140. Stone Game II_第2张图片

代码

int stoneGameII(vector& nums) {
    if(nums.empty())
        return 0;
        
    int n = nums.size();
    for(int i = n - 2; i >= 0; --i)
        nums[i] += nums[i + 1];
        
    int m = n + 1 >> 1;
    vector> dp(n, vector(m + 1, 0));
        
    for(int i = n - 1; i >= 0; --i)
    {
        for(int j = 1; j <= m; ++j)
        {
            if(n - i <= j << 1)
            {
                dp[i][j] = nums[i];
                continue;
            }
            int sm = nums[i];
            for(int k = 1; k <= j  << 1; ++k)
            {
                if(i + k >= n || k > m)
                    break;
                if(dp[i + k][max(k, j)] < sm)
                    sm = dp[i + k][max(k, j)];
            }
            dp[i][j] = nums[i] - sm;
        }
    }
    return dp[0][1];
}

你可能感兴趣的:(#,LeetCode,#,动态规划)