• 剑指offer-裴波那切类递归,动态规划题总结


    题1:斐波那契数列

    题目描述:

    大家都知道斐波那契数列,现在要求输入一个整数n,请你输出斐波那契数列的第n项。

    n<=39

    思路:这类题一般都是递归,带备忘的递归,动态规划这几种做法

     1 public class Solution {
     2     public int Fibonacci(int n) {
     3         if(n==0)return 0;
     4         if(n==1||n==2)return 1;
     5         int pre=1;
     6         int cur=1;
     7         int next=0;
     8         for(int i=3;i<=n;i++){
     9             next=cur+pre;  
    10             pre=cur;
    11             cur=next;        
    12         }
    13         return next;
    14     }
    15 }

    题2:跳台阶

    题目描述:

    一只青蛙一次可以跳上1级台阶,也可以跳上2级。求该青蛙跳上一个n级的台阶总共有多少种跳法。

     思路:这次用递归做

    1 public class Solution {
    2     public int JumpFloor(int target) {
    3         if(target<=1)return 1;
    4         if(target==2)return 2;
    5         return JumpFloor(target-1)+JumpFloor(target-2);
    6     }
    7 }

    跳台阶的同类题:

    上台阶:

    有一楼梯共m级,刚开始时你在第一级,若每次只能跨上一级或者二级,要走上m级,共有多少走法?注:规定从一级到一级有0种走法。

    给定一个正整数int n,请返回一个数,代表上楼的方式数。保证n小于等于100。为了防止溢出,请返回结果Mod 1000000007的值。

    测试样例:
    3
    返回:2
    思路:这次用动态规划和矩阵快速幂,思路见我代码里的注释
     1 import java.util.*;
     2 /*
     3 典型的裴波那切问题,用递归或是循环都可解决,主要是要找到递推关系式
     4 
     5  设x为要跳过的级数,f(x)为方式数
     6  当当前跳1级时,剩下的级数还有f(x-1)种
     7  当当前跳2级时,剩下的级数还有f(x-2)种
     8  所以总的方式有:f(x)=f(x-1)+f(x-2);
     9  利用此递推式便可求出总的方式数
    10  若采用矩阵快速幂法可先构造出初始矩阵
    11  |1 1|   |f(x-1)|   |f(x)  |
    12  |1 0| * |f(x-2)| = |f(x-1)|
    13  由
    14  |1 1|   |f(1)|   |f(2)|
    15  |1 0| * |f(0)| = |f(1)|
    16  |1 1|   |f(2)|   |f(3)|
    17  |1 0| * |f(1)| = |f(2)|
    18 ....
    19 所以可得
    20  {|1 1|}^(x-1)    |f(1)|   |f(x)  |
    21  {|1 0|}       *  |f(0)| = |f(x-1)|
    22 */
    23 public class GoUpstairs {
    24  /*   
    25     public int countWays(int n) {
    26         // 用动态规划
    27         long dp[]=new long[n+1];
    28         int m=1000000007;
    29         dp[0]=0;
    30         dp[1]=0;
    31         dp[2]=1;
    32         dp[3]=2;
    33        for(int i=4;i<=n;i++){
    34            dp[i]=(dp[i-1]+dp[i-2])%m;
    35        }
    36          
    37         return (int)dp[n];
    38            
    39     }
    40 */
    41     public int countWays(int n) {
    42         //用矩阵快速幂
    43         long[][] base={{1,1},{1,0}};
    44         long[][] unit={{1,1},{1,0}};
    45      
    46 /*   
    47         while(n!=0){
    48         if(n%2==1)
    49             unit= matrixMultiple(unit,base);
    50        else{
    51             base= matrixMultiple(base,base);
    52        }
    53             n=n/2;
    54         }
    55  */
    56         if(n<=1)return 0;
    57         for(int i=1;i<n-1;i++){
    58             base=matrixMultiple(base,unit);
    59         }
    60         return (int)(base[0][0]);
    61     }
    62     public  long[][] matrixMultiple(long[][] ret,long[][] base){
    63         //这个函数实质上就是实现ret与base的矩阵相乘
    64         long [][]tmp=new long [2][2];
    65         for(int i=0;i<2;i++){
    66             for(int j=0;j<2;j++){
    67                 tmp[i][j]=(ret[i][0]*base[0][j]+ret[i][1]*base[1][j])%1000000007;
    68             }
    69         }
    70         for(int i=0;i<2;i++){
    71             for(int j=0;j<2;j++){
    72                 ret[i][j]=tmp[i][j];}
    73         }
    74         return ret;
    75          
    76     }
    77 }

    题3;变态跳台阶

    题目描述:一只青蛙一次可以跳上1级台阶,也可以跳上2级……它也可以跳上n级。求该青蛙跳上一个n级的台阶总共有多少种跳法。

    思路:最后一节阶梯是必跳的,其他的都有跳或不跳两种情况,所以总的方法数就是2^(n-1)

    1 public class Solution {
    2     public int JumpFloorII(int target) {
    3         if(target==0)return 1;
    4         return 1<<--target;
    5     }
    6 }

    题4:放苹果

    题目描述:

    把 M 个同样的苹果放在 N 个同样的盘子里,允许有的盘子空着不放,问共有多少种不同的分法?
    注意:5、1、1 和 1、5、1 是同一种分法,即顺序无关。
    输入描述:
    输入包含多组数据。每组数据包含两个正整数 m和n(1≤m, n≤20)。
    输出描述:
    对应每组数据,输出一个整数k,表示有k种不同的分法。
    思路:
    对于这个题,分两种情况:
     
    如果有空盘子,相当于我先拿出一个盘子不用,用剩下的盘子去装苹果,方法数为:dp[m][n-1]
    如果没有空盘子,相当于我先给每个盘子里放一个苹果,然后再用剩下的苹果去放盘子,方法数为:dp[m-n][n],不过在在此处要注意满足m>=n,不然就会为负
    对于m<n的情况,苹果比盘子数还少,空盘子是没用的,去掉空盘子,那就等同于在n个盘子里放n个苹果
     
     
     
     1 import java.util.*;
     2 public class Main{
     3     private static final int  maxn=25;
     4     public static void main(String[] args){
     5         int [][]dp=new int[maxn][maxn];
     6         for(int i=0;i<maxn;i++){     
     7                 dp[i][1]=1;
     8                 dp[0][i]=1;
     9                 dp[1][i]=1;
    10         }
    11         for(int i=1;i<maxn;i++){
    12             for(int j=2;j<maxn;j++){
    13                 if(i>=j)
    14                     dp[i][j]=dp[i][j-1]+dp[i-j][j];
    15                 else if(i<j)
    16                     dp[i][j]=dp[i][i];
    17                 
    18                 
    19             }
    20         }       
    21         
    22         Scanner sc=new Scanner(System.in);
    23         while(sc.hasNext()){
    24         int m=sc.nextInt();
    25         int n=sc.nextInt();
    26         int res=0;
    27           
    28            System.out.println(dp[m][n]);    
    29     }
    30 }
    31 }
    放苹果题和整数划分类题是一样的
    整数划分:求将一个整数m至多划分成n个数有多少种情况
     
    变形:求将一个整数m划分成n个数有多少种情况
    dp[m][n] = dp[m-n][n] + dp[m-1][n-1]; 对于变形后的问题,存在两种情况:   
        1. n 份中不包含 1 的分法,为保证每份都 >= 2,可以先拿出 n 个 1 分到每一份,然后再把剩下的 m- n 分成 n 份即可,分法有: dp[m-n][n]        
        2. n 份中至少有一份为 1 的分法,可以先那出一个 1 作为单独的1份,剩下的 m- 1 再分成 n- 1 份即可,分法有:dp[m-1][n-1

    题5:换零钱

    题目描述:

    有一个数组changes,changes中所有的值都为正数且不重复。每个值代表一种面值的货币,每种面值的货币可以使用任意张,对于一个给定值x,请设计一个高效算法,计算组成这个值的方案数。

    给定一个int数组changes,代表所以零钱,同时给定它的大小n,另外给定一个正整数x,请返回组成x的方案数,保证n小于等于100且x小于等于10000。

    测试样例:
    [5,10,25,1],4,15
    返回:6
    测试样例:
    [5,10,25,1],4,0
    返回:1

    思路:

    这个题与之前做过的上楼梯,整数划分,篮子放苹果等的同属同一类题
    对于上楼梯类题型,对于n层楼梯,上的方式较少,大多只有2,3种,比如一次上1层,或是一次上2层
    则可以直接得到f(n)=f(n-1)+f(n-2);
    使用带备忘的递归,或是自底向上的dp都比较好解决
    对于整数划分类题型,将一个整数n分成m个数相加的形式,求有多少种分法
    直接对所分出的数中是否含有1,可得到2种情况
    即dp[n][m]=dp[n][m-1]+dp[n-m][m]
    对于此题,给定金额就好像是上面中待分的整数,只不过此时由于金额大小的限制,该整数只能由给定数组中的数组成

     1 import java.util.*;
     2 
     3 public class Exchange {
     4     public int countWays(int[] changes, int n, int x) {
     5         // write code here
     6         int [][] dp=new int[x+1][n];
     7         //dp[i][j]表示的是对于金额为i的,所给的钱的大小为change[0~j]中的数时,所对应的方案数
     8         for(int i=0;i*changes[0]<=x;i++){
     9             //第一列表示用大小为change[0]的钱去组成金额为i的方案数,
    10             //可知,只有当金额为change[0]的倍数时,才存在有一种方案数
    11             dp[i*changes[0]][0]=1;
    12         }
    13         for(int j=0;j<n;j++){
    14             //第一行表示金额为0时,各个钱数的组成方案,可知都只有一种方案
    15             dp[0][j]=1;
    16         }
    17         for(int i=1;i<=x;i++){
    18             for(int j=1;j<n;j++){
    19                 dp[i][j]=dp[i][j-1]+(i-changes[j]>=0?dp[i-changes[j]][j]:0);
    20             }
    21         }
    22         return dp[x][n-1];
    23     }
    24 }
  • 相关阅读:
    express开发实例
    node.js操作mongoDB数据库
    Mysql JDBC Url参数说明useUnicode=true&characterEncoding=UTF-8
    Hibernate 使用说明
    mysql 时间戳与日期格式的相互转换
    MOOC即Massive Open Online Course的缩写
    CentOS+Apache+php无法访问redis的解决方法
    CentOS 6.5下Redis安装详细步骤
    Linux进程基础
    CentOS如何查看端口是被哪个应用/进程占用
  • 原文地址:https://www.cnblogs.com/pathjh/p/9134943.html
Copyright © 2020-2023  润新知