No.59 - LeetCode1140 - 动态规划 - 很难

主要是状态不好想,一开始想的博弈dp
dpA[i][L],表示当前区间[L,N]先手拿i个的最优值;
dpB[j][L],表示当前区间后手拿j个的最优值。
然后用先手i,控制后手j的数量。

总是WA,后来想明白了,题读错了,
原因是M值的原因,这道题M一旦变大就不会变小。
而我的这种做法适合后手只依据前手拿的数量,即M值会根据先手变大或变小。

附上这种思路的代码:

// WA掉
int stoneGameII(vector<int>& piles){
    int N = piles.size();
    int dp[N+1][N+1]; // 表示dp[i][L],区间[L,N]上先手拿i个的最优值
    memset(dp,0,sizeof(dp));
    int sum[N+1];
    memset(sum,0,sizeof(sum));
    for(int i=N-1;i>=0;i--) sum[i] = sum[i+1] + piles[i];
    // 枚举区间
    for(int L=N-1;L>=0;L--){
        // 枚举先手,先手最大为剩下所有石头
        for(int i=1;i<=N-L;i++){
            // 枚举后手,后手最大为剩下所有石头,若石头没有,则后手可以为0
            int t = 0;
            for(int j=0;j<=min(i*2,N-L-i);j++){
                t = max(t,dp[j][L+i]);
            }
            dp[i][L] = sum[L] - t;
        }
    }
    return max(dp[1][0],dp[2][0]);
}

后手不能只依据先手拿的数量,还要依据M值,也就是说,只要先手拿不超过M,那后手可以拿的数量是不会变的。
但是这个dp区间需要从后往前扫,所以前面M值是未知的,不好处理状态。

所以一种直接的做法就是,以M和L作为状态,dp[m][L]表示当前区间[L,N]在上一次M更新为m时的最优解。

AC代码如下:

// AC
class Solution {
public:
    int stoneGameII(vector<int>& piles){
        int N = piles.size();
        int dp[N+1][N+1]; // dp[M][L] 表示当前区间[L,N]上,当M值时的最大
        memset(dp,0,sizeof(dp));
        int sum[N+1];
        memset(sum,0,sizeof(sum));
        for(int i=N-1;i>=0;i--) sum[i] = sum[i+1] + piles[i];
        // 枚举左边界L
        for(int L=N-1;L>=0;L--){
            // 枚举上次生成的M值
            for(int M=1;M<N;M++){
                // 枚举当前拿x个
                for(int x=1;x<=min(N-L,2*M);x++){
                    dp[M][L] = max(dp[M][L],sum[L] - dp[max(x,M)][L+x]);
                }
            }
        }
        return dp[1][0];
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值