动态规划2:台阶问题
2017-05-03 23:06
204 查看
题目:有n级台阶,一个人每次上一级或者两级,问有多少种走完n级台阶的方法。为了防止溢出,请将结果Mod 1000000007给定一个正整数int n,请返回一个数,代表上楼的方式数。保证n小于等于100000。
测试样例:1 返回:1
思路:这是一个实际问题,实际问题大多使用动态规划来解决。先分析问题:按照动态规划的套路,这个问题其实可以使用建立一个二维数组的方式来解决,即假设每次可以向上走的步数的元素是数组penny[2,3,4],需要走的台阶数目是n=aim,那么就等同于上面的零钱凑整问题,建立二维数组dp[i][j]表示使用penny[0~i]的元素来走到j级台阶的方案数目,先求出第1行是用penny[0]走完j级台阶,于是是0或者1,再计算第1列,即使用penny[0~i]来走完0级台阶,于是方案数是1,之后从上到下,从左网友进行计算,直到最后求出dp[n-1][aim]即为所求结果。
本题由于上台阶的可能只有1级或者2级这2种,因此是一个退化的动态规划问题,可以使用更加简单灵活的方式来解决问题。
本题可以直接使用递归来解决问题:
但是这属于暴力递归搜索算法,时间复杂度巨大,测试时当n=50就需要几分钟来计算,因此显然是不行的,为了降低时间复杂度,关键是减少重复计算,因此应该使用动态规划的方法,所谓动态规划方法就是将已经计算过的值保留下来以便下次计算需要用到时可以复用(思想等同于记忆搜索算法),因此实现这种将已经计算过的值保留以便复用的思策略有2种:
策略1:将每次中间计算得到的结果使用哈希表(map,一维数组,二维数组)进行保存,在使用时先取哈希表中寻找,找到直接利用找不到时再计算。-------记忆搜索算法
策略2:规定计算路径,总是将基础值在前面进行计算,后面的计算可以完全依赖于前面的计算结果得到,即按照这种计算路径时,在计算后面的结果时恰好前面的结果已经计算出来了,并且可以直接使用前面的计算结果。常见的计算路径有:一维数组从左到右;二维数组从上到下从左到右;斐波那契数列,台阶数列等从前到后---------动态规划算法
本题中根据直接观察的规律或者暴力搜索得到的规律f(n)=f(n-1)+f(n-2)可知,一个结果完全依赖于前面的2个计算结果,因此按照从前往后的计算路径,每次计算时使用前面的计算结果,同时将当前计算出来的结果保存到一个全局的数组中,就可以解决问题。当然这个问题很简单,可以不使用数组而仅仅使用一个全局的变量count来表示当前n的计算结果即可(因为本题是线性的,一个计算结果只会被使用一次,因此可以被覆盖,如果有可能被再次使用那么将其放入到一个数组中进行保留,即数组中可以保留每一个计算结果,不会被后面的计算结果覆盖,于是可以多次重复使用,这要根据具体的问题具体决定是使用二维数组、一维数组、还是一个变量)。
常识:为了避免溢出,常常对结果取余处理,可以像上面这样对每个可能发生溢出的变量进行取余,也可以针对性地只对可能发生溢出的变量作溢出处理,这里实际上溢出总是发生在2个n相加的时候,因此只要对temp进行取余即可,只要temp不溢出,显然n1,n2也不会溢出:
inttemp=(n1+n2)%1000000007;
n1=n2;
n2=temp;
测试样例:1 返回:1
思路:这是一个实际问题,实际问题大多使用动态规划来解决。先分析问题:按照动态规划的套路,这个问题其实可以使用建立一个二维数组的方式来解决,即假设每次可以向上走的步数的元素是数组penny[2,3,4],需要走的台阶数目是n=aim,那么就等同于上面的零钱凑整问题,建立二维数组dp[i][j]表示使用penny[0~i]的元素来走到j级台阶的方案数目,先求出第1行是用penny[0]走完j级台阶,于是是0或者1,再计算第1列,即使用penny[0~i]来走完0级台阶,于是方案数是1,之后从上到下,从左网友进行计算,直到最后求出dp[n-1][aim]即为所求结果。
本题由于上台阶的可能只有1级或者2级这2种,因此是一个退化的动态规划问题,可以使用更加简单灵活的方式来解决问题。
本题可以直接使用递归来解决问题:
import java.util.*; //台阶问题:递归解决 public class GoUpstairs { public static void main(String[] args) { System.out.println(new GoUpstairs().countWays(50)); } public int countWays(int n) { //特殊输入 if(n<=0) return 0; //边界条件 if(n==1) return 1; if(n==2) return 2; return countWays(n-1)%1000000007+countWays(n-2)%1000000007; } }
但是这属于暴力递归搜索算法,时间复杂度巨大,测试时当n=50就需要几分钟来计算,因此显然是不行的,为了降低时间复杂度,关键是减少重复计算,因此应该使用动态规划的方法,所谓动态规划方法就是将已经计算过的值保留下来以便下次计算需要用到时可以复用(思想等同于记忆搜索算法),因此实现这种将已经计算过的值保留以便复用的思策略有2种:
策略1:将每次中间计算得到的结果使用哈希表(map,一维数组,二维数组)进行保存,在使用时先取哈希表中寻找,找到直接利用找不到时再计算。-------记忆搜索算法
策略2:规定计算路径,总是将基础值在前面进行计算,后面的计算可以完全依赖于前面的计算结果得到,即按照这种计算路径时,在计算后面的结果时恰好前面的结果已经计算出来了,并且可以直接使用前面的计算结果。常见的计算路径有:一维数组从左到右;二维数组从上到下从左到右;斐波那契数列,台阶数列等从前到后---------动态规划算法
本题中根据直接观察的规律或者暴力搜索得到的规律f(n)=f(n-1)+f(n-2)可知,一个结果完全依赖于前面的2个计算结果,因此按照从前往后的计算路径,每次计算时使用前面的计算结果,同时将当前计算出来的结果保存到一个全局的数组中,就可以解决问题。当然这个问题很简单,可以不使用数组而仅仅使用一个全局的变量count来表示当前n的计算结果即可(因为本题是线性的,一个计算结果只会被使用一次,因此可以被覆盖,如果有可能被再次使用那么将其放入到一个数组中进行保留,即数组中可以保留每一个计算结果,不会被后面的计算结果覆盖,于是可以多次重复使用,这要根据具体的问题具体决定是使用二维数组、一维数组、还是一个变量)。
import java.util.*; //台阶问题:动态规划 public class GoUpstairs { public int countWays(int n) { //特殊输入 if(n<=0) return 0; //全局变量用来计算当前n的计算结果,由于n计算结果只会被复用1次,所以可以被覆盖,否则使用数组 int n1=1; int n2=2; //从前往后计算并保留结果 if(n==1) return 1; if(n==2) return 2; for(int i=3;i<=n;i++){ int temp=n1%1000000007+n2%1000000007; n1=n2%1000000007; n2=temp%1000000007; } return n2; } }
常识:为了避免溢出,常常对结果取余处理,可以像上面这样对每个可能发生溢出的变量进行取余,也可以针对性地只对可能发生溢出的变量作溢出处理,这里实际上溢出总是发生在2个n相加的时候,因此只要对temp进行取余即可,只要temp不溢出,显然n1,n2也不会溢出:
inttemp=(n1+n2)%1000000007;
n1=n2;
n2=temp;
相关文章推荐
- 问题一、走台阶问题(递归和动态规划)
- 牛客网台阶问题源码实现 动态规划
- 动态规划解决跳台阶问题
- 动态规划台阶问题
- 跳台阶问题 + 变态跳台阶问题 解法(动态规划递归 + 非递归)
- 动态规划--台阶问题
- 动态规划解决跳台阶问题
- 动态规划9:跳台阶问题
- PKU 1050 动态规划-解决最大子矩阵问题
- 动态规划 (Dynamic Programming) 之 背包问题合辑 (Knapsack, Subset Sum, Partition and change making problem )
- 动态规划解决最大子矩阵问题
- 动态规划——贪心算法——活动选择问题
- 动态规划求解最长公共子序列问题
- 动态规划解决矩阵链乘问题的java编码实现
- 动态规划——找零钱问题
- 使用动态规划解决有关数字组合的问题
- 动态规划求解编辑距离问题
- [POI][Ruvtex 31]潜水员问题(动态规划)
- 装箱问题(NOIP竞赛原题)(动态规划)
- 石子合并问题 --动态规划--解法1