面试题整理-台阶问题
2012-10-11 21:11
381 查看
原题
测试链接
http://ac.jobdu.com/problem.php?cid=1039&pid=4
首先我们考虑上面链接中提出的问题:由于一次只能走两步,那么在到达n步的上一步。其只能是要么在(n-1)个台阶上。要么是在(n-2)个台阶上。
所以我们可以得到fn = fn-1 + fn-2;
f1 = 1; f2 = 2;
这里可以看出,同样也可以利用斐波那契数的原理进行处理。(也有人直接把这个序列转换成为斐波那契数来处理,亦可以,但是对于后续的思路不是一个整体。在这里就不考虑。)
这里需要注意与HD OJ方法的不同之处。http://acm.hdu.edu.cn/showproblem.php?pid=2041
在HD OJ上面,由于起始步是在第一台阶上,走第二个台阶就只需要一步。所以是F1 = 0, F2 = 1; F3 = 2;
最简单的处理方式
当n<=50的时候,是可以利用64位int长整型。long long来进行处理。
很简单,就把上面那个题给秒掉了。
高效的处理
如果你还对前面讲到的斐波那契数有印象的话,可以轻易地得到。
[ Fn Fn-1] = [F2 F1] pow(A, n - 2);
A = [1 1
1 0];
值得注意的是,针对于原始的斐波那契数而言,其递推之后是到[F1 F0] 截止,所以其指数是n-1。而在这里是到[F2 F1] 截止。所以指数为n-2。
此外,假设T = pow(A, n-2);
与前面不同的是,Fn = F2*T[0][0] + F1*T[1][0];
从而可以写出如下代码:
大整数的处理
这里,还是可以利用前面提到的大整数的加法模板来进行运算。
扩展-1
首先题意变成:如果一下子可以走1,2,3步呢。也就是多了一种走法。根据惯性思维
Fn = Fn-1 + Fn-2 + Fn-3
且
F1 = 1
F2 = 2
F3 = F1 + F2 + 1 = 4
由于有了递推公式。可以轻易地写出如下代码:
同样也可以写成大数模板形式
那么我们考虑,如何写成O(lgN)的效率呢。
同样,我们也可以写出矩阵的表达式。
[Fn Fn-1 Fn-2] = [Fn-1 Fn-2 Fn3] * A;
可以得出A的值为
1 1 0
1 0 1
1 0 0
那么,在这种情况下,如果层次递推,我们可以得到
[Fn Fn-1 Fn-2] = [F3 F2 F1] * pow(A, n-3);
如果设T = pow(A, n-3);
那么Fn = F3*T[0,0] + F2*T[1,0] + F1*T[2,0];
因此,也可以写出如下的代码。
扩展-2
那么接下的问题是
假设A上台阶,一次可以跨1层,2层,3层..或m层,问A上n层台阶,有多少种走法?
其中,m和n都是正整数,并且 m <= n, m <= 10, n <= 50
由于m, n并不清楚。
不过对于1<= x <= m的走法,应该是很清楚的。也就是Fx = Fx-1 + Fx-2 + ...... + F1 + 1
而对于大于m的而言,则是Fy = Fy-1 + Fy-2 + Fy-3 + .... + Fy-m;
公式都出来了,就不用去想了。
如果觉得上面的方法不好理解。没关系。直接采用打表法。
这里也顺便给出大整数求解的方案,也就是当n超过50的时候的处理方式。
可能你还希望找出一个针对于存在m的情况下的高效率的解法。那好吧。这里给个示例。原理与前面O(lgN)的原理几乎一样。
只不过你需要注意一下A矩阵的表达形式。其他的没什么不一样的。还有就是最后有个相乘的情况。
扩展-3
一只青蛙一次可以跳上1级台阶,也可以跳上2级……它也可以跳上n级。求该青蛙跳上一个n级的台阶总共有多少种跳法。
测试链接:http://ac.jobdu.com/problem.php?cid=1039&pid=5
这个只是第二个扩展的特殊情况。也就是当m==n的时候的情况。
这个就没有更高效的办法了。除了再加入大整数之外。~~大整数就不用再加了。都差不多的。
当然,如果再深入地看一下,会发现F(n) = 2^(n-1);
那这个代码就更好写了。
测试链接
http://ac.jobdu.com/problem.php?cid=1039&pid=4
首先我们考虑上面链接中提出的问题:由于一次只能走两步,那么在到达n步的上一步。其只能是要么在(n-1)个台阶上。要么是在(n-2)个台阶上。
所以我们可以得到fn = fn-1 + fn-2;
f1 = 1; f2 = 2;
这里可以看出,同样也可以利用斐波那契数的原理进行处理。(也有人直接把这个序列转换成为斐波那契数来处理,亦可以,但是对于后续的思路不是一个整体。在这里就不考虑。)
这里需要注意与HD OJ方法的不同之处。http://acm.hdu.edu.cn/showproblem.php?pid=2041
在HD OJ上面,由于起始步是在第一台阶上,走第二个台阶就只需要一步。所以是F1 = 0, F2 = 1; F3 = 2;
最简单的处理方式
当n<=50的时候,是可以利用64位int长整型。long long来进行处理。
#include <stdio.h> #include <stdlib.h> long long a[71]; void init(void) { a[0] = 0; a[1] = 1; a[2] = 2; for (int i = 3; i < 71; ++i) { a[i] = a[i-1] + a[i-2]; } } int main(void) { int n;init(); while (scanf("%d", &n) != EOF) { printf("%lld\n", a ); } return 0; } //注意打表及长整型。
很简单,就把上面那个题给秒掉了。
高效的处理
如果你还对前面讲到的斐波那契数有印象的话,可以轻易地得到。
[ Fn Fn-1] = [F2 F1] pow(A, n - 2);
A = [1 1
1 0];
值得注意的是,针对于原始的斐波那契数而言,其递推之后是到[F1 F0] 截止,所以其指数是n-1。而在这里是到[F2 F1] 截止。所以指数为n-2。
此外,假设T = pow(A, n-2);
与前面不同的是,Fn = F2*T[0][0] + F1*T[1][0];
从而可以写出如下代码:
#include <stdio.h> #include <stdlib.h> typedef struct _node { long long a, b; long long c, d; } node; void _multiple(node *x, node *y) { node temp; temp.a = x->a*y->a + x->b*y->c; temp.b = x->a*y->b + x->b*y->d; temp.c = x->c*y->a + x->d*y->c; temp.d = x->c*y->b + x->d*y->d; *x = temp; } long long fib(int n) { if (0 == n) return 0; if (1 == n) return 1; if (2 == n) return 2; node odd; odd.a = odd.d = 1; odd.c = odd.b = 0; //单位矩阵 node temp; temp.a = temp.b = temp.c = 1; temp.d = 0; // A矩阵 n -= 2; while (n) { if (n&1) _multiple(&odd, &temp); _multiple(&temp, &temp); n >>= 1; } return odd.a * 2 + odd.c; } int main() { int n; while (scanf("%d", &n) != EOF) { printf("%lld\n", fib(n)); } return 0; }
大整数的处理
这里,还是可以利用前面提到的大整数的加法模板来进行运算。
#include <iostream> #include <string> #include <stdio.h> #include <stdlib.h> using namespace std; string &_del_zeros_before_dot(string &a) { if (a.length() <= 0 || a[0] != '0') return a; int i = 0; while (i < a.length() && a[i] == '0') ++i; a = a.substr(i, a.length() - i); return a; } string &_string_add_string(const string &a, const string &b, string &res) { int sum_value = 0, add_bit = 0; const int alen = a.length(), blen = b.length(); res = "0" + (alen > blen ? a : b); for (int i = alen-1, j = blen-1, k = res.length() - 1; i >= 0 || j >= 0 || add_bit > 0; --i, --j, --k){ sum_value = (i>=0 ? a[i]-48: 0) + (j>=0 ? b[j]-48: 0) + add_bit; add_bit = sum_value / 10; res[k] = sum_value%10 + '0'; } if (res[0] == '0') res = res.substr(1, res.length() - 1); return res; } string fib(int n) { if (0 == n) return "0"; if (1 == n) return "1"; if (2 == n) return "2"; string a_2 = "1", b_1 = "2", ret = "0"; for (int i = 3; i <= n; ++i) { _string_add_string(a_2, b_1, ret); // fn = fn-2 + fn-1; a_2 = b_1; b_1 = ret; } return ret; } int main(void) { int n; while (scanf("%d", &n) != EOF) { printf("%s\n", fib(n).c_str()); } return 0; }
扩展-1
首先题意变成:如果一下子可以走1,2,3步呢。也就是多了一种走法。根据惯性思维
Fn = Fn-1 + Fn-2 + Fn-3
且
F1 = 1
F2 = 2
F3 = F1 + F2 + 1 = 4
由于有了递推公式。可以轻易地写出如下代码:
#include <stdio.h> long long step(int n) { int i = 0; long long a = 1, b = 2, c = 4, ret; if (0 == n) return 0; if (1 == n) return 1; if (2 == n) return 2; if (3 == n) return 4; for (i = 4; i <= n; ++i) { ret = a + b + c; a = b; b = c; c = ret; } return ret; } int main(void) { int n; while (scanf("%d", &n) != EOF) { printf("%lld\n", step(n)); } return 0; }
同样也可以写成大数模板形式
#include <iostream> #include <string> #include <stdio.h> #include <stdlib.h> using namespace std; string &_del_zeros_before_dot(string &a) { if (a.length() <= 0 || a[0] != '0') return a; int i = 0; while (i < a.length() && a[i] == '0') ++i; a = a.substr(i, a.length() - i); return a; } string &_string_add_string(const string &a, const string &b, string &res) { int sum_value = 0, add_bit = 0; const int alen = a.length(), blen = b.length(); res = "0" + (alen > blen ? a : b); for (int i = alen-1, j = blen-1, k = res.length() - 1; i >= 0 || j >= 0 || add_bit > 0; --i, --j, --k){ sum_value = (i>=0 ? a[i]-48: 0) + (j>=0 ? b[j]-48: 0) + add_bit; add_bit = sum_value / 10; res[k] = sum_value%10 + '0'; } if (res[0] == '0') res = res.substr(1, res.length() - 1); return res; } string fib(int n) { if (0 == n) return "0"; if (1 == n) return "1"; if (2 == n) return "2"; if (3 == n) return "4"; string a = "1", b = "2", c = "4", ret = "0", temp="0"; for (int i = 4; i <= n; ++i) { _string_add_string(a, b, temp); // temp = fn-2 + fn-3; _string_add_string(temp, c, ret); // ret = temp + fn-1; a = b; b = c; c = ret; } return ret; } int main(void) { int n; while (scanf("%d", &n) != EOF) { printf("%s\n", fib(n).c_str()); } return 0; }
那么我们考虑,如何写成O(lgN)的效率呢。
同样,我们也可以写出矩阵的表达式。
[Fn Fn-1 Fn-2] = [Fn-1 Fn-2 Fn3] * A;
可以得出A的值为
1 1 0
1 0 1
1 0 0
那么,在这种情况下,如果层次递推,我们可以得到
[Fn Fn-1 Fn-2] = [F3 F2 F1] * pow(A, n-3);
如果设T = pow(A, n-3);
那么Fn = F3*T[0,0] + F2*T[1,0] + F1*T[2,0];
因此,也可以写出如下的代码。
#include <stdio.h> #include <stdlib.h> #include <string.h> #define MX 3 typedef struct _node { long long a[MX][MX]; } node; void _multiple(node *x, node *y) { node temp; long long (*a)[MX] = x->a; long long (*b)[MX] = y->a; long long (*c)[MX] = temp.a; for (int row = 0; row < MX; ++row) { for (int col = 0; col < MX; ++col) { long long s = 0; for (int i = 0; i < MX; ++i) { s += a[row][i] * b[i][col]; } c[row][col] = s; } } *x = temp; } long long fib(int n) { if (0 == n) return 0; if (1 == n) return 1; if (2 == n) return 2; if (3 == n) return 4; node odd; //设置odd为单位矩阵 memset(odd.a, 0, sizeof(odd.a)); for (int i = 0; i < MX; ++i) { odd.a[i][i] = 1; } node temp; // A矩阵 memset(temp.a, 0, sizeof(temp.a)); for (int i = 0; i < MX; ++i) { temp.a[i][0] = 1; if (i + 1 < MX) temp.a[i][i+1] = 1; } n -= 3; while (n) { if (n&1) _multiple(&odd, &temp); _multiple(&temp, &temp); n >>= 1; } return 4*odd.a[0][0] + 2*odd.a[1][0] + odd.a[2][0]; } int main() { int n; while (scanf("%d", &n) != EOF) { printf("%lld\n", fib(n)); } return 0; }
扩展-2
那么接下的问题是
假设A上台阶,一次可以跨1层,2层,3层..或m层,问A上n层台阶,有多少种走法?
其中,m和n都是正整数,并且 m <= n, m <= 10, n <= 50
由于m, n并不清楚。
不过对于1<= x <= m的走法,应该是很清楚的。也就是Fx = Fx-1 + Fx-2 + ...... + F1 + 1
而对于大于m的而言,则是Fy = Fy-1 + Fy-2 + Fy-3 + .... + Fy-m;
公式都出来了,就不用去想了。
#include <stdio.h> #include <iostream> #include <list> using namespace std; long long fib(unsigned int m, int n) { list<long long> l; long long a = 0, s = 0, ret; for (int i = 1; i <= m && i <= n; ++i) { a = s + 1; l.push_back(a); s += a; } if (n <= m) return l.back(); for (int i = m + 1; i <= n; ++i) { s = 0; for (list<long long>::iterator iter = l.begin(); iter != l.end(); ++iter) { s += *iter; } ret = s; l.pop_front(); l.push_back(ret); } return ret; } int main(void) { int m, n; while (scanf("%d%d", &m, &n) != EOF){ printf("%lld\n", fib(m, n)); } return 0; }
如果觉得上面的方法不好理解。没关系。直接采用打表法。
#include <stdio.h> int m, n; long long a[51]; long long fib(int m, int n) { long long s = 0; a[0] = 0; for (int i = 1; i <=m && i <= n; ++i) { a[i] = s + 1; s += a[i]; } if (n <= m) return a ; for (int i = m + 1; i <= n; ++i) { s = 0; for (int j = 1; j <= m; ++j) { s += a[i-j]; } a[i] = s; } return a ; } int main(void) { while (scanf("%d%d", &m, &n) != EOF &&0 <= m && m <=n && n <= 50) { printf("%lld\n", fib(m, n)); } return 0; }
这里也顺便给出大整数求解的方案,也就是当n超过50的时候的处理方式。
#include <iostream> #include <string> #include <stdio.h> #include <stdlib.h> #include <list> using namespace std; string &_del_zeros_before_dot(string &a) { if (a.length() <= 0 || a[0] != '0') return a; int i = 0; while (i < a.length() && a[i] == '0') ++i; a = a.substr(i, a.length() - i); return a; } string &_string_add_string(const string &a, const string &b, string &res) { int sum_value = 0, add_bit = 0; const int alen = a.length(), blen = b.length(); res = "0" + (alen > blen ? a : b); for (int i = alen-1, j = blen-1, k = res.length() - 1; i >= 0 || j >= 0 || add_bit > 0; --i, --j, --k){ sum_value = (i>=0 ? a[i]-48: 0) + (j>=0 ? b[j]-48: 0) + add_bit; add_bit = sum_value / 10; res[k] = sum_value%10 + '0'; } if (res[0] == '0') res = res.substr(1, res.length() - 1); return res; } string fib(unsigned int m, int n) { list<string> l; string a = "0", s = "0", ret, temp; for (int i = 1; i <= m && i <= n; ++i) { _string_add_string(s, "1", a); l.push_back(a); _string_add_string(s, a, temp); s = temp; } if (n <= m) return l.back(); for (int i = m + 1; i <= n; ++i) { s = "0"; for (list<string>::iterator iter = l.begin(); iter != l.end(); ++iter) { _string_add_string(s, *iter, temp); s = temp; } ret = s; l.pop_front(); l.push_back(ret); } return ret; } int main(void) { int m, n; while (scanf("%d%d", &m, &n) != EOF) { printf("%s\n", fib(m, n).c_str()); } return 0; }
可能你还希望找出一个针对于存在m的情况下的高效率的解法。那好吧。这里给个示例。原理与前面O(lgN)的原理几乎一样。
只不过你需要注意一下A矩阵的表达形式。其他的没什么不一样的。还有就是最后有个相乘的情况。
#include <stdio.h> #include <stdlib.h> #include <string.h> typedef struct _node { long long **a; }node; long long **_init(int m) { long long **t = NULL; t = (long long **)malloc(sizeof(long long*)*m); for (int i = 0; i < m; ++i){ t[i] = (long long *)malloc(sizeof(long long) * m); for (int j = 0; j < m; ++j) { t[i][j] = 0; } } return t; } void _destroy(long long **t, int m) { for (int i = 0; i < m; ++i) { free(t[i]); } free(t); } void _multiple(node *x, node *y, int m) { node temp; temp.a = _init(m); long long **a = x->a; long long **b = y->a; long long **c = temp.a; for (int row = 0; row < m; ++row) { for (int col = 0; col < m; ++col) { long long s = 0; for (int i = 0; i < m; ++i) { s += a[row][i] * b[i][col]; } c[row][col] = s; } } _destroy(x->a, m); x->a = temp.a; } long long fib(int m, int n) { long long ret; long long *front = (long long *) malloc(sizeof(long long)*(m + 1)); long long s = 0; front[0] = 0; for (int i = 1; i <= m && i <= n; ++i) { front[i] = s + 1; s += front[i]; } if (n <= m) { ret = front ; free(front); return ret; } node odd; odd.a = _init(m); //设置odd为单位矩阵 for (int i = 0; i < m; ++i) { odd.a[i][i] = 1; } //A矩阵. node temp; temp.a = _init(m); for (int i = 0; i < m; ++i) { temp.a[i][0] = 1; if (i + 1 < m) temp.a[i][i+1] = 1; } n -= m; while (n) { if (n&1) _multiple(&odd, &temp, m); _multiple(&temp, &temp, m); n >>= 1; } ret = 0; for (int i = 1; i <= m; ++i) { ret += front[i] * odd.a[m-i][0]; } _destroy(odd.a, m); _destroy(temp.a, m); return ret; } int main() { int m, n; while (scanf("%d%d", &m, &n) != EOF) { printf("%lld\n", fib(m, n)); } return 0; }
扩展-3
一只青蛙一次可以跳上1级台阶,也可以跳上2级……它也可以跳上n级。求该青蛙跳上一个n级的台阶总共有多少种跳法。
测试链接:http://ac.jobdu.com/problem.php?cid=1039&pid=5
这个只是第二个扩展的特殊情况。也就是当m==n的时候的情况。
#include <stdio.h> #include <stdlib.h> #include <string.h> long long fib(int m) { long long ret; long long *front = (long long *) malloc(sizeof(long long)*(m + 1)); long long s = 0; front[0] = 0; for (int i = 1; i <= m; ++i) { front[i] = s + 1; s += front[i]; } ret = front[m]; free(front); return ret; } int main() { int m, n; while (scanf("%d", &n) != EOF) { printf("%lld\n", fib(n)); } return 0; }
这个就没有更高效的办法了。除了再加入大整数之外。~~大整数就不用再加了。都差不多的。
当然,如果再深入地看一下,会发现F(n) = 2^(n-1);
那这个代码就更好写了。
#include <stdio.h> long long pow(long long x, long long n) { long odd = 1; while (n) { if (n&1) odd *= x; x *= x; n >>= 1; } return odd; } int main(void) { int n; while (scanf("%d", &n) != EOF) { printf("%lld\n", pow(2, n-1)); } return 0; }
相关文章推荐
- 面试题整理-卡特兰数问题收集
- BAT大厂Android面试题Java部分问题和答案整理(一)
- 面试题之青蛙变态跳台阶问题
- 面试题整理3 大数的表示及加减法问题
- 【Killua笔试面试题整理】最大间隔问题
- 【Killua笔试面试题整理】查找问题苹果
- 剑指offer面试题9 斐波那契数列及青蛙跳台阶问题
- 【面试题】N阶台阶的走法种数问题(分支思想)
- 程序员面试题精选100题(23)-跳台阶问题
- 关于SSH框架中的错误整理,遇到的问题都是前进的台阶
- 斐波那契数列——腾讯面试题台阶问题
- JS面试题---关于算法台阶的问题
- 剑指offer面试题9-青蛙跳台阶及其变种问题
- 程序员面试题精选100题(23)-跳台阶问题
- 跳台阶问题分析整理
- 程序员面试题100题第23题——跳台阶问题
- 面试题:青蛙跳台阶问题
- 程序员面试题100题第23题——跳台阶问题
- 23. 微软面试题:跳台阶问题
- JS面试题-算法台阶问题