以Jump Game为例,逐层击破动态规划

本文以LeetCode上Jump Game为案例,分别用递归法、递归+动态规划、动态规划完成解答。
(本文由DianeSoHungry原创,转载请说明出处!https://www.cnblogs.com/DianeSoHungry/p/11406212.html)
语言:C++

题目

Given an array of non-negative integers, you are initially positioned at the first index of the array.
Each element in the array represents your maximum jump length at that position.
Determine if you are able to reach the last index.

Example 1:

Input: [2,3,1,1,4]
Output: true
Explanation: Jump 1 step from index 0 to 1, then 3 steps to the last index.

Example 2:

Input: [3,2,1,0,4]
Output: false
Explanation: You will always arrive at index 3 no matter what. Its maximum jump length is 0, which makes it impossible to reach the last index.

递归法

思路是这样的:

若你被放在下标为i的位置,判断你能到达终点的依据是“从当前位置往后到i+nums[i]位置,是否至少存在一个位置,从这个位置你能到达终点。

//////recursive backtracking////////
#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;

bool canJump(int* nums, int n){
    if (n==1) {
        return true;
    }
    if (nums[0]==0) {
        return false;
    }
    bool res = false;
    for (int i = 1; i <= nums[0]; i++) {
        res = res || canJump(&nums[i], n-i);
    }
    return res;
}

int main(){
    int n;
    cin >> n;
    int nums[n];
    for (int i = 0; i < n; i++) {
        int num;
        scanf("%d", &num);
        nums[i] = num;
    }
    cout << canJump(nums, n);
    return 0;
}

递归 + 动态规划 (Top-down)

单纯递归造成的问题是:重复。例如:nums=[5, 3, 4, 2]。位置2的返回既要被位置0调用,还要被位置1调用。加上memo记忆表使得当

////////recursion +  memoization table////////
#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;

bool canJump (int* nums, int* memo, int n) {
    if (memo[0] != -1) {
        return memo[0];
    }
    if (n == 1) {
        memo[0] = 1;
        return true;
    }
    if (nums[0]==0) {
        memo[0] = 0;
        return false;
    }
    bool res = false;
    for (int i = 1; i <= nums[0]; i++) {
        res = res || canJump(&nums[i], &memo[i], n-i);
    }
    memo[0] = res;
    return res;
}

int main(){
    int n;
    cin >> n;
    int nums[n];
    int memo[n];
    for (int i = 0; i < n; i++) {
        int num;
        scanf("%d", &num);
        nums[i] = num;
        memo[i] = -1;
    }
    cout << canJump(nums, memo, n);
    return 0;
}

动态规划(Bottom-up)

该方法超时,下文利用原数组作为记忆表解决了超时问题。

////////Bottom-up Dynamic Programming////////
#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;

bool canJump(vector<int> &nums) {
    vector<bool> memo;
    for (int i = 0; i < nums.size(); i ++) {
        memo.push_back(false);
    }
    memo.back() = true;
    for (int i = nums.size() - 2; i > -1; i--) {
        for (int j = 1; j <= nums[i] && i+j < nums.size(); j++) {
            memo[i] = memo[i] || memo[i+j];
            if (memo[i] == true) {
                break;
            }
        }
    }
    return memo[0];
    
}

int main(){
    int n;
    cin >> n;
    vector<int> nums;
    for (int i = 0; i < n; i++) {
        int num;
        scanf("%d", &num);
        nums.push_back(num);
    }
    cout << canJump(nums);
    return 0;
}

动态规划(Bottom-up & Inplace)

////////Bottom-up Dynamic Programming////////
#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;

bool canJump(vector<int> &nums){
    nums.back() = 1;
    for (int i = nums.size() - 2; i > -1; i--) {
        int tmp = 0;
        for (int j = 1; j <= nums[i] && i+j < nums.size(); j++) {
            tmp = tmp || nums[i+j];
            if (tmp) {
                break;
            }
        }
        nums[i] = tmp;
        for (auto k:nums) {
            cout << k;
        }
        cout << endl;
    }
    return nums[0];
}

int main(){
    int n;
    cin >> n;
    vector<int> nums;
    for (int i = 0; i < n; i++) {
        int num;
        scanf("%d", &num);
        nums.push_back(num);
    }
    cout << canJump(nums);
    return 0;
}
原文地址:https://www.cnblogs.com/DianeSoHungry/p/11406212.html