一维动态规划根据转移方程,复杂度通常有两种状况。git
func(i) 只和 func(i-1)有关,时间复杂度是O(n),这种状况下空间复杂度每每能够优化为O(1)数组
func(i) 和 func(1~i-1)有关,时间复杂度是O(n*n),这种状况下空间复杂度通常没法优化,依然为O(n)app
本篇讨论第一种状况大数据
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步,而不是给定的几个不连续的值,这样咱们能够经过定义最长可达距离这个转移方程来简化思想。
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; } };