石子游戏-系列DP

文章目录486. 预测赢家方法一 直观迭代方法二 记忆化方法三 动态规划石子游戏石子游戏 VII石子游戏 III486. 预测赢家题目????给定一个表示分数的非负整数数组。 玩家 1 从数组任意一端拿取一个分数,随后玩家 2 继续从剩余数组任意一端拿取分数,然后玩家 1 拿,…… 。每次一个玩家只能拿取一个分数,分数被拿取之后不再可取。直到没有剩余分数可取时游戏结束。最终获得分数总和最多的玩家获胜。给定一个表示分数的数组,预测玩家1是否会成为赢家。你可以假设每个玩家的玩法都会使他的分数最大化。
摘要由CSDN通过智能技术生成

486. 预测赢家

题目🔗
给定一个表示分数的非负整数数组。 玩家 1 从数组任意一端拿取一个分数,随后玩家 2 继续从剩余数组任意一端拿取分数,然后玩家 1 拿,…… 。每次一个玩家只能拿取一个分数,分数被拿取之后不再可取。直到没有剩余分数可取时游戏结束。最终获得分数总和最多的玩家获胜。

给定一个表示分数的数组,预测玩家1是否会成为赢家。你可以假设每个玩家的玩法都会使他的分数最大化。

关键就是自己的得分减去对方可能拿到的最高分,然后进行一轮轮迭代

方法一 直观迭代

class Solution {
public:
    bool PredictTheWinner(vector<int>& nums) {
        int len = nums.size();
    
        return helper(nums, 0 , nums.size()-1) >= 0;
        
    }
    int helper(vector<int>& nums, int i, int j){
        if(i == j)return nums[i];
        
        int a = nums[i] - helper(nums, i+1, j);
        int b = nums[j] - helper(nums, i, j - 1);
       
        return max(a, b);
    }
};


方法二 记忆化

class Solution {
    vector<vector<int>>dp;
public:
    bool PredictTheWinner(vector<int>& nums) {
        int len = nums.size();
        dp.resize(len, vector<int>(len));
        return helper(nums, 0 , nums.size()-1) >= 0;
        
    }
    int helper(vector<int>& nums, int i, int j){
        if(i == j)return nums[i];
        if(dp[i][j] != 0) return dp[i][j];
        int a = nums[i] - helper(nums, i+1, j);
        int b = nums[j] - helper(nums, i, j - 1);
        dp[i][j] = max(a, b);
        return max(a, b);
    }
};

方法三 动态规划

class Solution {
    vector<vector<int>>dp;
public:
    bool PredictTheWinner(vector<int>& nums) {
        int len = nums.size();
        dp.resize(len, vector<int>(len));
        for(int i = 0; i < len; i++)dp[i][i] = nums[i];
        for(int i = len - 2; i >= 0; i--){
            for(int j = i + 1; j < len; j++){
                dp[i][j] = max(nums[i] - dp[i+1][j], nums[j] - dp[i][j-1]);
            }
        }
        return dp[0][len-1] >= 0;
        
    }
    
};

石子游戏

题目🔗
亚历克斯和李用几堆石子在做游戏。偶数堆石子排成一行,每堆都有正整数颗石子 piles[i] 。

游戏以谁手中的石子最多来决出胜负。石子的总数是奇数,所以没有平局。

亚历克斯和李轮流进行,亚历克斯先开始。 每回合,玩家从行的开始或结束处取走整堆石头。 这种情况一直持续到没有更多的石子堆为止,此时手中石子最多的玩家获胜。

假设亚历克斯和李都发挥出最佳水平,当亚历克斯赢得比赛时返回 true ,当李赢得比赛时返回 false 。

上一题动态规划的基础上,相同的模板

class Solution {
public:
    bool stoneGame(vector<int>& piles) {
        int len = piles.size();
        vector<vector<int>>dp(len, vector<int>(len, 0));
        for(int i = 0; i < len; i++)dp[i][i] = piles[i];
        for(int i = len - 2; i >= 0; i--){
            for(int j = i + 1; j < len; j++){
                dp[i][j] = max(piles[i] - dp[i+1][j], piles[j] - dp[i][j-1]);
            }
        }
        return dp[0][len-1] >= 0;
    }
};

石子游戏 VII

题目🔗

石子游戏中,爱丽丝和鲍勃轮流进行自己的回合,爱丽丝先开始 。

有 n 块石子排成一排。每个玩家的回合中,可以从行中 移除 最左边的石头或最右边的石头,并获得与该行中剩余石头值之 和 相等的得分。当没有石头可移除时,得分较高者获胜。

鲍勃发现他总是输掉游戏(可怜的鲍勃,他总是输),所以他决定尽力 减小得分的差值 。爱丽丝的目标是最大限度地 扩大得分的差值 。

给你一个整数数组 stones ,其中 stones[i] 表示 从左边开始 的第 i 个石头的值,如果爱丽丝和鲍勃都 发挥出最佳水平 ,请返回他们 得分的差值 。

得分机制改变,剩余石头之和,模板没有改变

class Solution {
public:
    int stoneGameVII(vector<int>& stones) {
        int len = stones.size();
        vector<int>pre(len+1, 0);
        vector<vector<int>>dp(len, vector<int>(len,0));
        for(int i = 0; i < len; i++){
            pre[i+1] = pre[i] + stones[i];
        }
        for(int i = len - 2; i >= 0; i--){
            for(int j = i + 1; j < len; j++){
                if(j == i + 1){
                    dp[i][j] = max(stones[i], stones[j]);
                    continue;
                }else{
                    int a = pre[j + 1] - pre[i + 1] - dp[i+1][j];
                    int b = pre[j] - pre[i] - dp[i][j-1];
                    dp[i][j] = max(a, b);
                }
            }
        }
        return dp[0][len - 1];
    }
};

石子游戏 III

题目🔗

Alice 和 Bob 用几堆石子在做游戏。几堆石子排成一行,每堆石子都对应一个得分,由数组 stoneValue 给出。

Alice 和 Bob 轮流取石子,Alice 总是先开始。在每个玩家的回合中,该玩家可以拿走剩下石子中的的前 1、2 或 3 堆石子 。比赛一直持续到所有石头都被拿走。

每个玩家的最终得分为他所拿到的每堆石子的对应得分之和。每个玩家的初始分数都是 0 。比赛的目标是决出最高分,得分最高的选手将会赢得比赛,比赛也可能会出现平局。

假设 Alice 和 Bob 都采取 最优策略 。如果 Alice 赢了就返回 “Alice” ,Bob 赢了就返回 “Bob”,平局(分数相同)返回 “Tie” 。

拿的方式本来是,i或者j(开头还是结尾), 现在变成了,i, i + 1, i + 2,三种选择,依旧是选择最高的得分。

class Solution {
public:
    string stoneGameIII(vector<int>& stoneValue) {
        int len = stoneValue.size();
        vector<vector<int>>dp(len, vector<int>(len, 0));
        for(int i = 0; i < len; i++)dp[i][i] = stoneValue[i];
        for(int i = len - 2; i >= 0; i--){
            for(int j = i + 1; j < len; j++){
                if(j == i+ 1){
                    int a = stoneValue[i] - dp[i+1][j];
                    int b = stoneValue[i] + stoneValue[j];
                    dp[i][j] = max(a, b);
                }else if( j == i + 2){
                    int a = stoneValue[i] - dp[i+1][j];
                    int b = stoneValue[i] + stoneValue[i + 1] - dp[i+2][j]; 
                    int c = stoneValue[i] + stoneValue[i + 1] + stoneValue[i + 2];
                    dp[i][j] = max(a, max(b, c));
                }else{
                    int a = stoneValue[i] - dp[i + 1][j];
                    int b = stoneValue[i] + stoneValue[i + 1] - dp[i+2][j];
                    int c = stoneValue[i] + stoneValue[i + 1] + stoneValue[i + 2] - dp[i + 3][j];
                    dp[i][j] = max(a, max(b, c));
                }
            }
        }
        if(dp[0][len - 1] == 0)return "Tie";
        else if(dp[0][len - 1] > 0)return "Alice";
        return "Bob";
    }
};

超时优化

不能从结尾拿数据,因此数据只能是来自开头区间,因此减少一个维度。
f[i] 表示是一个比较值,表示在区间[i,i+1…n-1]内,当前玩家比另一位玩家最多能多拿的石子的数量,如果这个差值是正数,说明当前玩家比另一位玩家拿的多,差值为0则表示一样多,负数则表示拿的少

f[i]= max(sum[i:j-1]-f[j]) j∈[i+1:i+3]


class Solution {
public:
    string stoneGameIII(vector<int>& stoneValue) {
        int len = stoneValue.size();
        vector<int>dp(len+1, INT_MIN);
        dp[len] = 0;
        for(int i = len - 1; i >= 0; i--){
            int sumpre = 0;
            for(int k = 0; k < 3; k++){
                if(i + k < len){
                    sumpre += stoneValue[i + k];
                    dp[i] = max(dp[i], sumpre - dp[i + k + 1]);
                } 
            }
        
        }
        if(dp[0] == 0)return "Tie";
        else if(dp[0] > 0)return "Alice";
        return "Bob";
    }
};

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值