题目地址:
https://www.lintcode.com/problem/climbing-stairs-iii/description
有个人想位于第 0 0 0级台阶,他想走到第 n n n级台阶,给定一个正整数数组 A A A,当他位于第 i i i级台阶的时候,他可以一次走 1 ∼ A [ i ] 1\sim A[i] 1∼A[i]级台阶。问他走到第 n n n级台阶有多少个不同的方案。
思路是差分数组。传统的方法是,开一个数组 c c c, c [ i ] c[i] c[i]表示到台阶 i i i的方案数。首先 c [ 0 ] = 1 c[0]=1 c[0]=1,表示到达 0 0 0级台阶只有一个方案(就是不走),然后遍历 A A A,每次遍历到 A [ i ] A[i] A[i]的时候,就将 c [ i + 1 : i + A [ i ] ] c[i+1:i+A[i]] c[i+1:i+A[i]]这个区间的所有数都增加 c [ i ] c[i] c[i]。最后返回 c [ n ] c[n] c[n]即可。这里注意我们要频繁对一个区间进行加法,用差分数组可以加速这个操作。整体复杂度可以降到 O ( n ) O(n) O(n)。设 d d d是 c c c的差分数组,即 c [ i ] = ∑ j = 0 i d [ j ] c[i]=\sum_{j=0}^{i}d[j] c[i]=∑j=0id[j],那么 d [ i ] = c [ i ] − c [ i − 1 ] , d [ 0 ] = c [ 0 ] d[i]=c[i]-c[i-1],d[0]=c[0] d[i]=c[i]−c[i−1],d[0]=c[0]。这样要将 c [ u : v ] c[u:v] c[u:v]加上 x x x,等价于将 d [ u ] d[u] d[u]变为 d [ u ] + x d[u]+x d[u]+x,将 d [ v + 1 ] d[v+1] d[v+1]变为 d [ v + 1 ] − x d[v+1]-x d[v+1]−x。这样每次将区间加 x x x这个操作的复杂度就是 O ( 1 ) O(1) O(1)了。初始 c = ( 1 , 0 , 0 , . . . ) c=(1,0,0,...) c=(1,0,0,...),所以 d = ( 1 , − 1 , 0 , 0 , . . . ) d=(1,-1,0,0,...) d=(1,−1,0,0,...)。代码如下:
public class Solution {
/**
* @param n: the number of steps
* @param num: the maximum step Ming can run up at the ith step
* @return: Return the number of ways to run up the stairs
*/
public long Solve(int n, int[] num) {
// Write your code here
// c的长度实际上是n + 1,所以差分数组d的长度也是n + 1
long[] diff = new long[n + 1];
long MOD = (long) 1E9 + 7;
// 初始化一下d数组
diff[0] = 1;
diff[1] = -1;
// res维护的是c[i]
long res = 0;
for (int i = 0; i < n; i++) {
res = (res + diff[i]) % MOD;
// 算一下要增加的区间
int l = i + 1, r = Math.min(n, i + num[i]);
if (l < diff.length) {
diff[l] = (diff[l] + res) % MOD;
}
if (r + 1 < diff.length) {
diff[r + 1] = (diff[r + 1] - res) % MOD;
}
}
// 算c[n]的时候最后要将d[n]加上去
res = (res + diff[n]) % MOD;
return res;
}
}
时空复杂度 O ( n ) O(n) O(n)。