動態規劃小結 - 一維動態規劃 - 時間複雜度 O(n),題 [LeetCode] Jump Game,Decode Ways

引言

一維動態規劃根據轉移方程,複雜度通常有兩種狀況。git

func(i) 只和 func(i-1)有關,時間複雜度是O(n),這種狀況下空間複雜度每每能夠優化爲O(1)數組

func(i) 和 func(1~i-1)有關,時間複雜度是O(n*n),這種狀況下空間複雜度通常沒法優化,依然爲O(n)app

本篇討論第一種狀況大數據

 

例題 1

Jump Game優化

Given an array of non-negative integers, you are initially positioned at the first index of the array.spa

Each element in the array represents your maximum jump length at that position..net

Determine if you are able to reach the last index.code

For example:
A = [2,3,1,1,4], return true.blog

A = [3,2,1,0,4], return false.element

class Solution {
public:
    bool canJump(int A[], int n) {
    }
};

 

這道題目確定是用DP來作。

我一開始的想法爲定義bool reachable[n] 數組,reachable[i] = true 表示第 i 元素能夠到達末尾。

所以reachable[i] = if(reachable[i+1] == true || reachable[i+2] == true || ...|| reachable[i+A[i]] == true)

返回reachable[0]即爲答案。

可是若是按這種思路寫,須要用一個二維循環來完成整個過程,時間複雜度依然爲O(n2)

按這種思路寫出來的代碼:

class Solution {
public:
    bool canJump(int A[], int n) {
        if(n <= 1) return true;
        bool *reachable = new bool[n-1];
        if(A[0] >= (n-1)) return true;
        for(int i = n-2; i >= 0; --i){
            if(A[i] >= (n-1-i)) reachable[i] = true;
            else{
                int j;
                for(j = 1; j <= A[i]; ++j){
                    if(reachable[i+j]){
                        reachable[i] = true;
                        break;
                    }
                }
                if(j > A[i]) reachable[i] = false;
            }
        }
        return reachable[0];
    }
};

 LeetCode上大數據是過不了的,超時。

 

網上參考了 http://blog.csdn.net/xiaozhuaixifu/article/details/13628465 的博文後,明白了上面這種思路的狀態轉移方程之因此效率低,是由於用bool 做爲數組元素,這種思路自己就不是一個動態規劃中推薦的思路。動態規劃爲了節省時間,每每儘量地利用數組來存儲最大量的信息,bool值只能存true和false。

改進版的思路是:這個數組再也不單純地存可達或不可達這樣的bool值,而是存儲從0位置出發的最大可達長度。定義數組int canStillWalk[n],canStillWalk[i]表示到達 i 位置後,依然有餘力走出的最大長度。若是canStillWalk[i] < 0,表示走不到位置i。

狀態轉移方程爲:

canStillWalk[i] = max(canStillWalk[i-1], A[i-1]) - 1;

這樣咱們在計算canStillWalk[i]時,就再也不須要循環。

時間複雜度O(n), 空間複雜度 O(n)

class Solution {
public:
    bool canJump(int A[], int n) {
        if(n <= 1) return true;
        if(A[0] >= (n-1)) return true;
        int *canStillWalk = new int[n];
        canStillWalk[0] = A[0];
        for(int i = 1; i < n; ++i){
            canStillWalk[i] = max(canStillWalk[i-1], A[i-1]) - 1;
            if(canStillWalk[i] < 0) return false;
        }
        return canStillWalk[n-1] >= 0;
    }
};

 

接着能夠再簡化,由於canStillWalk[i] 只和 canStillWalk[i-1]相關,那麼咱們就不須要定義一個數組來存放消息了,直接用pre和 cur就能夠搞定,時間複雜度O(n), 空間複雜度 O(1)

class Solution {
public:
    bool canJump(int A[], int n) {
        if(n <= 1) return true;
        if(A[0] >= (n-1)) return true;
        int pre = A[0], cur = 0;
        for(int i = 1; i < n; ++i){
            cur = max(pre, A[i-1]) - 1;
            if(cur < 0) return false;
            pre = cur;
        }
        return cur >= 0;
    }
};

  

小結

對於動態規劃的題,狀態轉移方程比較重要,這道題的特殊性在於"步數連續",就是說,A[i] = s,代表從A[i] 能夠走1~s步,而不是給定的幾個不連續的值,這樣咱們能夠經過定義最長可達距離這個轉移方程來簡化思想。

 

例題 2

Decode Ways

A message containing letters from A-Z is being encoded to numbers using the following mapping:

'A' -> 1
'B' -> 2
...
'Z' -> 26

Given an encoded message containing digits, determine the total number of ways to decode it.

For example,
Given encoded message "12", it could be decoded as "AB" (1 2) or "L" (12).

The number of ways decoding "12" is 2.

 

時間複雜度O(n), 空間複雜度 O(1) 解法

class Solution {
public:
    int numDecodings(string s) {
        if(s.empty()) return 0;
        int pre = -1, cur = 1, tmp;
        for(int i = 1; i <= s.length(); ++i){
            tmp = cur;
            if(s[i-1] == '0'){
                if(isCode(s, i-2, i-1)) cur = pre;
                else return 0;
            }else if(isCode(s, i-2, i-1))
                cur += pre;;
            pre = tmp;
        }
        return cur;
    }
private:
    bool isCode(string &s, int i, int j){
        if(i < 0) return false;
        if(s[i] >= '3' || s[i] == '0') return false;
        if(s[i] == '2' && s[j] >= '7') return false;
        return true;
    }
};
相關文章
相關標籤/搜索