LeetCode算法练习——动态规划入门(二)

LeetCode746. 使用最小花费爬楼梯

数组的每个索引作为一个阶梯,第 i个阶梯对应着一个非负数的体力花费值 cost[i](索引从0开始)。每当你爬上一个阶梯你都要花费对应的体力花费值,然后你可以选择继续爬一个阶梯或者爬两个阶梯。您需要找到达到楼层顶部的最低花费。在开始时,你可以选择从索引为 0 或 1 的元素作为初始阶梯。

示例 1:

输入: cost = [10, 15, 20]
输出: 15
解释: 最低花费是从cost[1]开始,然后走两步即可到阶梯顶,一共花费15。

 示例 2:

输入: cost = [1, 100, 1, 1, 1, 100, 1, 1, 100, 1]
输出: 6
解释: 最低花费方式是从cost[0]开始,逐个经过那些1,跳过cost[3],一共花费6。

在看该题之前我们先来看下面这一小题:

LeetCode70. 爬楼梯 && 面试题10- II. 青蛙跳台阶问题

假设你正在爬楼梯。需要 n 阶你才能到达楼顶。每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?

注意:给定 n 是一个正整数。

示例 1:

输入: 2
输出: 2
解释: 有两种方法可以爬到楼顶。
1.  1 阶 + 1 阶
2.  2 阶

示例 2:

输入: 3
输出: 3
解释: 有三种方法可以爬到楼顶。
1.  1 阶 + 1 阶 + 1 阶
2.  1 阶 + 2 阶
3.  2 阶 + 1 阶

此题我们很容易得到递推式F(n) = F(n - 1) + F(n - 2)

class Solution {
public:
    int climbStairs(int n) {//f(n) = f(n - 1) + f(n - 2)
        vector<int> dp(n + 1);
        if(n < 2)  return n;
        else{
            dp[0] = 1;
            dp[1] = 1;
            for(int i = 2; i <= n; i++)
                dp[i] = dp[i - 1] + dp[i -2];
        }
        return dp[n];
    }
};

LeetCode 面试题 08.01. 三步问题

有个小孩正在上楼梯,楼梯有n阶台阶,小孩一次可以上1阶、2阶或3阶。实现一种方法,计算小孩有多少种上楼梯的方式。结果可能很大,你需要对结果模1000000007。

此题F(n) = F(n - 1) + F(n - 2) + F(n - 3),注意F(3)的初值不是3。

class Solution {
public:
    int waysToStep(int n) {
        if(n == 0)    return 0;
        else if(n == 1) return 1;
        else if(n == 2) return 2;
        else if(n == 3) return 4;//需要注意的是上三阶台阶的特殊情况
        vector<int> dp(n+1);
        dp[0] = 0;
        dp[1] = 1;
        dp[2] = 2;
        dp[3] = 4;
        for(int i = 4; i <= n; i++)
            dp[i] = ((dp[i - 1]%1000000007 + dp[i - 2]%1000000007)%1000000007 + dp[i - 3]%1000000007)%1000000007;
        return dp[n];
    }
};

本题思路

本题是上一类题的变种,增加了爬楼梯代价开销的计算,以及起点可以从索引0开始也可以从索引1开始的条件。

  • 由于起始点的条件发生了变化,因此初值设立:dp[0] = cost[0],dp[1] = cost[1],dp的容量为cost数组的大小;
  • 既然是计算最小开销,那么我们需要计算最后一步完成前只走一步的情况和最后一步完成前只走两步情况,比较其最小值,即确定了状态方程dp[i] = min(dp[i - 1] + cost[i], dp[i - 2] + cost[i] );
  • 当计算完成时我们返回dp[length - 1], dp[length -2]中的最小值,这是因为:当我们从索引0位置以1步或者2步的方式到达length-1的位置时,同样可以从索引1位置开始以1步或者2步的方式到达length终点位置时,因此我们需要比较0~length-1位置和0~length位置的开销值,取得最小值。
class Solution {
public:
    int minCostClimbingStairs(vector<int>& cost) {
        int length = cost.size();
        vector<int> dp(length);
        dp[0] = cost[0];
        dp[1] = cost[1];
        for(int i = 2; i < length; i++){
            dp[i] = min(dp[i - 1] + cost[i], dp[i - 2] + cost[i] );
        }
        return min(dp[length - 1], dp[length -2]);
    }
};

LeetCode面试题 16.17. 连续数列

给定一个整数数组,找出总和最大的连续数列,并返回总和。

示例:

输入: [-2,1,-3,4,-1,2,1,-5,4]
输出: 6
解释: 连续子数组 [4,-1,2,1] 的和最大,为 6。

状态: dp[i]表示以i结尾的最大连续子序列
状态转移:

  • 对于当前的nums[i],如果nums[i-1] >= 0 则 dp[i] = dp[i-1] + nums[i];
  • 否则 dp[i] = nums[i];

其实我们可以把nums当做dp数组,直接在原数组上面操作,这样可以省掉O(n)的空间

class Solution {
public:
    int maxSubArray(vector<int>& nums) {
        if(nums.size() == 0) return INT_MIN; 
        int maxSum = nums[0];
        for(int i = 1; i < nums.size(); i++)
        {
            if(nums[i - 1] >= 0)
                nums[i] += nums[i - 1];
            maxSum = max(maxSum, nums[i]);
        }
        return maxSum;
    }
};

LeetCode1025. 除数博弈

爱丽丝和鲍勃一起玩游戏,他们轮流行动。爱丽丝先手开局。

最初,黑板上有一个数字 N 。在每个玩家的回合,玩家需要执行以下操作:

  •     选出任一 x,满足 0 < x < N 且 N % x == 0 。
  •     用 N - x 替换黑板上的数字 N 。

如果玩家无法执行这些操作,就会输掉游戏。只有在爱丽丝在游戏中取得胜利时才返回 True,否则返回 false。假设两个玩家都以最佳状态参与游戏。

示例 1:

输入:2
输出:true
解释:爱丽丝选择 1,鲍勃无法进行操作。

示例 2:

输入:3
输出:false
解释:爱丽丝选择 1,鲍勃也选择 1,然后爱丽丝无法进行操作。

其实,在该题中,只要数为偶数即可胜利,但题目的本意还是用动态规划来解题,根据玩家执行操作很容易得到状态转移方程:N % x == 0 && f(N - x) == false,代码如下:

class Solution {
public:
    bool divisorGame(int N) {       
        //能整除且对手无法操作
        //N % x == 0 && f(N - x) == false
        if (N < 2) return false;
        vector<bool> dp(N + 1, false);
        dp[2] = true;
        for(int i = 3; i <= N; i++){
            for(int j = 1; j < i; j++){
                if(i%j == 0 && dp[i - j] == false){
                    dp[i] = true;
                    break;
                }
            }
        }
        return dp[N];
    }
};

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章