《预测赢家》—从递归到动态规划的优化之路

目录

题目:

递归:

记忆化递归:

动态规划:

动态规划(空间优化版):

总结: 


题目:

这是力扣的一条题目:

给定一个表示分数的非负整数数组。玩家1从数组任意一端拿取一个分数,随后玩家2继续从剩余数组任意一端拿取分数,然后玩家1拿....每次一个玩家只能拿取一个分数,分数被拿取之后不再可取。直到没有剩余分数可取时游戏结束。最终获得分数总和最多的玩家获胜。
给定一个表示分数的数组,预测玩家1是否会成为嬴家。你可以假设每个玩家的玩法都会使他的分数最大化。
 

不太会的可以自己去看题解,里边讲的很详细。

我这里主要写下我自己从递归一直优化到动态规划(空间优化)的历程。(代码可以通过力扣的oj)

递归:

class Solution {
public:
    int dfs(vector<int>&nums,int left,int right)
    {//这里dfs的意义是当前操作的玩家分数与另一个玩家的分数之差
        if(left == right)
            return nums[left];
        return max(nums[left] - dfs(nums,left+1,right),nums[right]-dfs(nums,left,right-1));
    }//由于"玩家的玩法都会使他的分数最大化,所以当前玩家尽量选择自己比对方分数高的策略
    bool PredictTheWinner(vector<int>& nums) {
            return dfs(nums,0,nums.size()-1) >=0;//自己的分数与对方的分数之差不小于0.则自己的分数不小于对方的分数
    }
};

 时间效率很低,于是进行升级:

记忆化递归:

class Solution {
public:
    int memory[20][20];//既然数据范围已经给定了那么直接定义数组大小
    int dfs(vector<int>&nums,int left,int right)
    {//这里dfs的意义是当前操作的玩家分数与另一个玩家的分数之差
        if(memory[left][right]!=-1)
            return memory[left][right];
        if(left == right)
            {
            memory[left][right]=nums[left];
            return nums[left];
            }
        int ans =  max(nums[left] - dfs(nums,left+1,right),nums[right]-dfs(nums,left,right-1));
        memory[left][right] = ans;
        return ans;
    }//由于"玩家的玩法都会使他的分数最大化,所以当前玩家尽量选择自己比对方分数高的策略
    bool PredictTheWinner(vector<int>& nums) {
            memset(memory,-1,sizeof(memory));
            return dfs(nums,0,nums.size()-1) >=0;//自己的分数与对方的分数之差不小于0.则自己的分数不小于对方的分数
    }
};

效率大大提高

然后通过总结递归的递推公式,不难联想到用动态规划代替递归:

动态规划:

 

class Solution {
public:
    int dp[20][20];
    bool PredictTheWinner(vector<int>& nums) {
        for(int i = 0;i<nums.size();++i)
        {
            dp[i][i] = nums[i];//初始化
        }
        for(int step = 1;step<nums.size();++step)
        {
            for(int i = 0;i+step<nums.size();++i)
            {
                int j = i + step;
                dp[i][j]=max(nums[i]-dp[i+1][j],nums[j]-dp[i][j-1]); //建议结合表格理解
            }
        }
        return dp[0][nums.size()-1] >=0;
    }
};

虽然原理相同,但是递归便于理解,动态规划代码优雅。

其实动态规划的代码还可以更加优雅!通过优化,dp数组可以减少至一维,进一步减少内存需求。

动态规划(空间优化版):

class Solution {
public:
    int dp[20];
    bool PredictTheWinner(vector<int>& nums) {
        for(int i = 0;i<nums.size();++i)
        {
            dp[i]= nums[i];//初始化
        }
        for(int step = 1;step<nums.size();++step)
        {
            for(int i = 0;i+step<nums.size();++i)
            {
                int j = i + step;
                dp[i]=max(nums[j]-dp[i],nums[i]-dp[i+1]); //建议结合表格理解
            }
        }
        return dp[0] >=0;
    }
};

 这下代码就非常之nice

执行结果:


总结: 

        在一般思路的基础上不断精进。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

咖啡咖_CoffCa

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值