LeetCode 5379. 石子游戏 III

https://leetcode-cn.com/problems/stone-game-iii/

 

方法一:枚举+记录历史结果

每次操作有3种选择,选择1个,选择2个,选择3个。枚举这3种操作,然后选择最优选择,就是比对方的分数越多越好。

选择了后,轮到对方选择(这里选择是枚举),不断循环,直到,剩下两个或一个。只有两个的情况下,判断第二个是否大于0,大于0要,小于0留给对方。只有1个的情况下,按照游戏规则,必须选择这一个。然后返回去,按照对手的最优选择结果来计算自己的结果。

记录历史结果,由于两个选手是对称的,对于同一个开始位置(一系列数字),得到的最优选择都是同样的,和结果无关,所以我们记录这个开始位置的结果。

class Solution {
public:
    int n;
    int record[2];
    int cache[50005][2];

    void select(const vector<int>& stoneValue ,int start,int mode){
        if(start>=n){
            return;
        }
        int other_mode=mode==0?1:0;

        if(cache[start][0]!=-1 && cache[start][1]!=-1){
            record[mode]=cache[start][0];
            record[other_mode]=cache[start][1];
            return;
        }

        
        
        if(n-start==1){
            record[mode]+=stoneValue[start];
        }
        else if(n-start==2){

            record[mode]+=stoneValue[start];
            if(stoneValue[start+1]>0){
                record[mode]+=stoneValue[start+1];
            }
            else{
                record[other_mode]+=stoneValue[start+1];
            }
        }
        else if(n-start>=3){

            int best_diff=-INT_MAX;
            int best_record[2];
            
            record[0]=record[1]=0;
            select(stoneValue,start+1,other_mode);
            record[mode]+=stoneValue[start];
            if(record[mode]-record[other_mode]>best_diff){
                best_diff=record[mode]-record[other_mode];
                best_record[0]=record[0];
                best_record[1]=record[1];
            }

            
            record[0]=record[1]=0;
            select(stoneValue,start+2,other_mode);
            record[mode]+=stoneValue[start];
            record[mode]+=stoneValue[start+1];

            if(record[mode]-record[other_mode]>best_diff){
                best_diff=record[mode]-record[other_mode];
                best_record[0]=record[0];
                best_record[1]=record[1];
            }

            record[0]=record[1]=0;
            select(stoneValue,start+3,other_mode);
            record[mode]+=stoneValue[start];
            record[mode]+=stoneValue[start+1];
            record[mode]+=stoneValue[start+2];
            if(record[mode]-record[other_mode]>best_diff){
                best_diff=record[mode]-record[other_mode];
                best_record[0]=record[0];
                best_record[1]=record[1];
            }

            record[0]=best_record[0];
            record[1]=best_record[1];
            
        }
        
        
        cache[start][0]=record[mode];
        cache[start][1]=record[other_mode];
        
    }

    string stoneGameIII(vector<int>& stoneValue) {
        memset(cache,-1,sizeof(cache));
        n=stoneValue.size();
        
        record[0]=record[1]=0;
        select(stoneValue,0,0);

        if(record[0]>record[1])
            return "Alice";
        else if(record[0]<record[1])
            return "Bob";
        else
            return "Tie";
    }
};

 

方法二:动态规划

dp[i],从位置i开始能得到的最好分数,由于两个人的分数总和就是石头分数总和,用了一个前缀和,另一个人的分数就总和减去一个人的分数。

状态转移也是选择3种选择中最好的,已经选择的分数加上下一步自己的分数。

class Solution {
public:
    int dp[50005];
    int sum[50005];

    string stoneGameIII(vector<int>& stoneValue) {
        memset(dp,0,sizeof(dp));
        memset(sum,0,sizeof(sum));
        int n=stoneValue.size();

        sum[0]=stoneValue[0];
        for(int i=1;i<n;i++){
            sum[i]=sum[i-1]+stoneValue[i];
        }
        
        for(int i=n-1;i>=0;i--){
            dp[i]=stoneValue[i]+sum[n-1]-sum[i]-(i+1>=n?0:dp[i+1]);
            if(i+1<n)
                dp[i]=max(dp[i],stoneValue[i]+stoneValue[i+1]+sum[n-1]-sum[i+1]-(i+2>=n?0:dp[i+2]));
            if(i+2<n)
                dp[i]=max(dp[i],stoneValue[i]+stoneValue[i+1]+stoneValue[i+2]+sum[n-1]-sum[i+2]-(i+3>=n?0:dp[i+3]));
        }
        if(dp[0]>sum[n-1]-dp[0])
            return "Alice";
        else if(dp[0]<sum[n-1]-dp[0])
            return "Bob";
        else
            return "Tie";
    }
};

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值