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

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

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

附上这种思路的代码:

// WA掉
int stoneGameII(vector& 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& 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

你可能感兴趣的:(leetcode)