LeetCode 877. 石子游戏 (DP、数学思维)

石子游戏

  • DP
    这里记录一下思考DP的过程。
    在这里插入图片描述
    石子堆的个数只会两个两个地增加,这自然可以作为我们DP的阶段划分。
    d p [ l ] [ r ] [ 0 ] , d p [ l ] [ r ] [ 1 ] dp[l][r][0],dp[l][r][1] 来分别表示序列 a [ l ] a[l] ~ a [ r ] a[r] 的石子堆,爱丽丝先手取第一个石子堆和最后一个石子堆能都获得的最大值。
    我们以 d p [ l ] [ r ] [ 0 ] dp[l][r][0] 为例分析,也就是分析[l~r]的序列,爱丽丝先手取第一个石子堆能够获得的最大值。
    既然爱丽丝取了a[l],那么李就要么取a[l+1],要么取a[r],而这分别对应上图第③、第②种情况的依赖,取完之后,就是分析相同类似情况的子结构了。
    但要注意李要保证自己的决策最优,就要是依赖的子结构Alice可以获得的式子尽可能地少。

那么DP就是:
d p [ l ] [ r ] [ 0 ] = m i n ( m a x ( d p [ l + 1 ] [ r 1 ] [ 0 ] , d p [ l + 1 ] [ r 1 ] [ 1 ] ) , m a x ( d p [ l + 2 ] [ r ] [ 0 ] , d p [ l + 2 ] [ r ] [ 1 ] ) ) dp[l][r][0]=min(max(dp[l+1][r-1][0],dp[l+1][r-1][1]),max(dp[l+2][r][0],dp[l+2][r][1]))
类似的 d p [ l ] [ r ] [ 1 ] dp[l][r][1] 也可以写出。
当然很明显,我们可以直接用 d p [ l ] [ r ] dp[l][r] 表示序列l~r爱丽丝能够获得的最大值,这样代码更清晰。

class Solution {
public:
    bool stoneGame(vector<int>& a) {
        int n = a.size(), sum = 0;
        for(int x:a){
            sum += x;
        }
        vector<vector<int>> dp(n,vector<int>(n));
        for(int l=0;l+1<n;l++){
            dp[l][l+1] = max(a[l],a[l+1]);
        }
        for(int k = 4;k<=n;k+=2){
            for(int l=0;l+k-1<n;l++){
                int r = l+k-1;
                int s1 =  a[l]+min(dp[l+2][r],dp[l+1][r-1]);
                int s2 =  a[r]+min(dp[l][r-2],dp[l+1][r-1]);
                dp[l][r] = max(s1,s2);
            }
        }
        return max(dp[0][n-1],dp[0][n-1])>sum/2;
    }
};
  • 数学思维
    可以用归纳的方式证明,先手的人必赢(这个和堆数为偶数有相当的关系)
    于是直接返回true就可以了。
    这里也就不证了,很容易想通,但不容易直接想到,而且对以后的做题帮助也不大。

猜你喜欢

转载自blog.csdn.net/qq_44846324/article/details/107527937
今日推荐