leetcode 石子游戏合集(动态规划)

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

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

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

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

思路:
首先找到状态转移方程:
dp[i][j]=max(piles[i]-dp[i+1][j],piles[j]-dp[i][j-1]);
表示:一堆从 ij 的石子
当先手拿 i ,那么后手从 i+1j 中拿(最优的方法拿)
当先手拿 j ,那么后手从 ij-1 中拿(最优的方法拿)

代码如下:

class Solution {
public:
    bool stoneGame(vector<int>& piles) {
        int n=piles.size();
        vector<vector<int>>dp(n,vector<int>(n,0));
        for(int i=0;i<n;i++){
            dp[i][i]=piles[i];
        }
        for(int d=1;d<n;d++){//计算两个石子......
            for(int j=0;j<n-d;j++){
                dp[j][j+d]=max(piles[j]-dp[j+1][j+d],piles[d+j]-dp[j][j+d-1]);
            }
        }
        return dp[0][n-1]>0;
    }
};

二、LeetCode 1140 石子游戏II
题目描述:
亚历克斯和李继续他们的石子游戏。许多堆石子 排成一行,每堆都有正整数颗石子 piles[i]。游戏以谁手中的石子最多来决出胜负。

亚历克斯和李轮流进行,亚历克斯先开始。最初,M = 1。

在每个玩家的回合中,该玩家可以拿走剩下的 前 X 堆的所有石子,其中 1 <= X <= 2M。然后,令 M = max(M, X)。

游戏一直持续到所有石子都被拿走。

假设亚历克斯和李都发挥出最佳水平,返回亚历克斯可以得到的最大数量的石头。

代码如下:

class Solution {
public:
    int stoneGameII(vector<int>& piles) {
        int n=piles.size();
        int sum=0;
        vector<vector<int>>dp(n,vector<int>(n+1,0));
        for(int i=n-1;i>=0;i--){
            sum+=piles[i];
            for(int M=1;M<=n;M++){
                if(i+2*M>=n){
                    dp[i][M]=sum;
                }
                else{
                    for(int x=1;x<=2*M;x++){
                        dp[i][M]=max(dp[i][M],sum-dp[i+x][max(M,x)]);
                    }
                }
            }
        }
        return dp[0][1];
    }
};

三、LeetCode 1406 石子游戏III
题目描述:
Alice 和 Bob 用几堆石子在做游戏。几堆石子排成一行,每堆石子都对应一个得分,由数组 stoneValue 给出。

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

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

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

代码如下:

class Solution {
public:
    string stoneGameIII(vector<int>& stoneValue) {
        int n=stoneValue.size();
        vector<int>sum(n+1,0);
        vector<int>dp(n+1,INT_MIN);
        for(int i=n-1;i>=0;i--){
            sum[i]=sum[i+1]+stoneValue[i];
        }
        dp[n]=0;
        for(int i=n-1;i>=0;i--){
            // int cur=0;
            for(int j=i;j<n&&j<=i+2;j++){
                // cur+=stoneValue[j];
                dp[i]=max(dp[i],sum[i]-dp[j+1]);
            }
        }
        if(dp[0]==sum[0]-dp[0]) return "Tie";
        if(dp[0]>sum[0]-dp[0]) return "Alice";
        return "Bob";
    }
};

四、LeetCode 1510 石子游戏IV
题目描述:
Alice 和 Bob 两个人轮流玩一个游戏,Alice 先手。

一开始,有 n 个石子堆在一起。每个人轮流操作,正在操作的玩家可以从石子堆里拿走 任意 非零 平方数 个石子。

如果石子堆里没有石子了,则无法操作的玩家输掉游戏。

给你正整数 n ,且已知两个人都采取最优策略。如果 Alice 会赢得比赛,那么返回 True ,否则返回 False 。

代码如下:

class Solution {
public:
    bool winnerSquareGame(int n) {
        vector<bool>state(n+1,false);
        for(int i=1;i<=n;i++){
            int s=sqrt(i);
            if(s*s==i){
                state[i]=true;
            }
            else{
                for(int j=1;j*j<i;j++){
                    if(state[i-j*j]==false){
                        state[i]=true;
                        break;
                    }
                }
            }
        }
        return state[n];
    }
};

五、LeetCode 1563 石子游戏V
题目描述:
几块石子 排成一行 ,每块石子都有一个关联值,关联值为整数,由数组 stoneValue 给出。

游戏中的每一轮:Alice 会将这行石子分成两个 非空行(即,左侧行和右侧行);Bob 负责计算每一行的值,即此行中所有石子的值的总和。Bob 会丢弃值最大的行,Alice 的得分为剩下那行的值(每轮累加)。如果两行的值相等,Bob 让 Alice 决定丢弃哪一行。下一轮从剩下的那一行开始。

只 剩下一块石子 时,游戏结束。Alice 的分数最初为 0 。

返回 Alice 能够获得的最大分数 。

思路:
dfs+记忆
(时间复杂度 n^3)
代码如下:

class Solution {
    vector<vector<int>>f;
public:
    int dfs(vector<int>&nums,int left,int right){
        if(left==right){
            return 0;
        }
        if(f[left][right]){
            return f[left][right];
        }
        int sum=0;
        for(int i=left;i<=right;i++){
            sum+=nums[i];
        }
        int sum_left=0;
        for(int i=left;i<right;i++){
            sum_left+=nums[i];
            int sum_right=sum-sum_left;
            if(sum_left>sum_right){
                f[left][right]=max(f[left][right],dfs(nums,i+1,right)+sum_right);
            }
            else if(sum_left<sum_right){
                f[left][right]=max(f[left][right],dfs(nums,left,i)+sum_left);
            }
            else{
                f[left][right]=max(f[left][right],max(dfs(nums,left,i),dfs(nums,i+1,right))+sum_left);
            }
        }
        return f[left][right];
    }
    int stoneGameV(vector<int>& stoneValue) {
        int n=stoneValue.size();
        vector<int>temp(n,0);
        for(int i=0;i<n;i++){
            f.push_back(temp);
        }
        return dfs(stoneValue,0,n-1);
    }
};

六、LeetCode 1686 石子游戏VI
题目描述:
Alice 和 Bob 轮流玩一个游戏,Alice 先手。

一堆石子里总共有 n 个石子,轮到某个玩家时,他可以 移出 一个石子并得到这个石子的价值。Alice 和 Bob 对石子价值有 不一样的的评判标准 。双方都知道对方的评判标准。

给你两个长度为 n 的整数数组 aliceValues 和 bobValues 。aliceValues[i] 和 bobValues[i] 分别表示 Alice 和 Bob 认为第 i 个石子的价值。

所有石子都被取完后,得分较高的人为胜者。如果两个玩家得分相同,那么为平局。两位玩家都会采用 最优策略 进行游戏。

请你推断游戏的结果,用如下的方式表示:

如果 Alice 赢,返回 1 。
如果 Bob 赢,返回 -1 。
如果游戏平局,返回 0 。

思路:
要不自己获得最大的价值,
要不自己获得最小的价值,让对手不能获得最大的价值

代码如下:

class Solution {
public:
    int stoneGameVI(vector<int>& aliceValues, vector<int>& bobValues) {
        vector<pair<int,int>>value;
        for(int i=0;i<aliceValues.size();i++){
            value.emplace_back(aliceValues[i]+bobValues[i],i);
        }
        sort(value.rbegin(),value.rend());//降序
        int sum_alice=0;
        int sum_bob=0;
        for(int i=0;i<aliceValues.size();i++){
            if(i%2==0){
                sum_alice+=aliceValues[value[i].second];
            }
            else{
                sum_bob+=bobValues[value[i].second];
            }
        }
        if(sum_alice>sum_bob) return 1;
        else if(sum_alice<sum_bob) return -1;
        return 0;
    }
};

七、LeetCode 1690 石子游戏VII
题目描述:
石子游戏中,爱丽丝和鲍勃轮流进行自己的回合,爱丽丝先开始 。

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

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

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

思路:
找到状态转移方程
dp[i][j]=max(sum[i+1][j]-dp[i+1][j],sum[i][j-1]-dp[i][j-1])
因此首先需要计算区间和
然后动态规划

代码如下:

class Solution {
public:
    int stoneGameVII(vector<int>& stones) {
        int n=stones.size();
        vector<vector<int>>sum(n,vector<int>(n));
        vector<vector<int>>dp(n,vector<int>(n));
        for(int i=0;i<n;i++){
            for(int j=i;j<n;j++){
                if(i==j) sum[i][i]=stones[i];
                else{
                    sum[i][j]=sum[i][j-1]+stones[j];
                }
            }
        }
        for(int i=n-1;i>=0;i--){
            for(int j=i;j<n;j++){
                if(i==j) dp[i][j]=0;
                else dp[i][j]=max(sum[i+1][j]-dp[i+1][j],sum[i][j-1]-dp[i][j-1]);

            }
        }
        return dp[0][n-1];
    }
};
  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值