1. 程式人生 > >梯有N階,上樓可以一步上一階,也可以一次上二階(Java實現)

梯有N階,上樓可以一步上一階,也可以一次上二階(Java實現)

走樓梯問題
                組合數學和動態規劃演算法

本文嘗試對“走樓梯”問題做一個較為系統的解釋。

程式碼可以自己複製出去,除錯執行和理解!



例3:一共有10級,每次可走一步也可以走兩步.必須要8步走完10級樓梯. 問:一共有多少種走法?
分析:走一步的需要6次,走兩步的需要2次。因此,本題是6個1、2個2的組合問題。在6個一步中,插入2個兩步的,因可放在第一個1步之前,也可以放在最後一個1步之後,所以6個1步有7個空.因此,如果兩個兩步在一起有c(7,1)種;如果兩個兩步的分開來插有C(7,2)種,因此共有
    c(7,1)+c(7,2)=7+21=28(種)=C(8,2)=C(8,6) 
    總數=8步中選2中走兩步的=8步中選6個走一步的
 
Java程式設計實現:(陣列迭代,動態規劃,遞迴)
package com.test;
public classzoutaijie {
// 梯有N階,上樓可以一步上一階,也可以一次上二階。編一個程式,計算共有多少種不同的走法。如果上20階會有幾種走法
public staticlongresult[]=new long[100];
public staticvoidmain(String[] args) {
result[0]=result[1]=1;
for(inti=2;i<=result.length;i++)
result[i]=-1;
//s不能太大,否則int溢位
int s =60;
//動態規劃
long startTime = System.currentTimeMillis();
System.out.println("動態規劃解決:"+fun1(s));
long endTime = System.currentTimeMillis();
System.out.println("動態規劃解決-程式執行時間:"+(endTime-startTime)+"ms");
 
//陣列疊加
long startTime2 = System.currentTimeMillis();
System.out.println("陣列疊加實現:"+fun2(s));
long endTime2 = System.currentTimeMillis();
System.out.println("陣列疊加實現-程式執行時間:"+(endTime2-startTime2)+"ms");
 
//遞迴方法
long startTime1 = System.currentTimeMillis();
System.out.println("遞迴方法解決:"+fun(s));
long endTime1 = System.currentTimeMillis();
System.out.println("遞迴方法解決-程式執行時間:"+(endTime1-startTime1)+"ms");
}
 
 
 
public staticlongfun(ints){
if(s==0 || s==1)
return 1;
else{
return fun(s-1)+fun(s-2);
}
 
}
 
public staticlongfun1(ints){
if(result[s]>=0) {
return result[s];
}else{
result[s]=(fun1(s-1)+fun1(s-2));
return result[s];
}
}
 
 
public staticlongfun2(ints){
long result_1[]=newlong[s+1];//注意這個要大一個,多了個第0個
result_1[0]=result_1[1]=1;
for(inti=2;i<=s;i++)
result_1[i]=result_1[i-1]+result_1[i-2];
return result_1[s];//s就是第s+1個
}
 
}




分析:
s=48和s=60輸出結果,顯然陣列疊加和動態規劃效率高很多很多,不是一個數量級的!

變形:如果每次可以走一步,2步,3步。。。。N步,那一共有多少種?

1)這裡的f(n) 代表的是n個臺階有一次1,2,...n階的 跳法數。

2)n = 1時,只有1種跳法,f(1) = 1

3) n = 2時,會有兩個跳得方式,一次1階或者2階,這回歸到了問題(1) ,f(2) = f(2-1) + f(2-2) 

4) n = 3時,會有三種跳得方式,1階、2階、3階,

    那麼就是第一次跳出1階後面剩下:f(3-1);第一次跳出2階,剩下f(3-2);第一次3階,那麼剩下f(3-3)

    因此結論是f(3) = f(3-1)+f(3-2)+f(3-3)

5) n = n時,會有n中跳的方式,1階、2階...n階,得出結論:

    f(n) = f(n-1)+f(n-2)+...+f(n-(n-1)) + f(n-n) => f(0) + f(1) + f(2) + f(3) + ... + f(n-1)

6) 由以上已經是一種結論,但是為了簡單,我們可以繼續簡化:

    f(n-1) = f(0) + f(1)+f(2)+f(3) + ... + f((n-1)-1) = f(0) + f(1) + f(2) + f(3) + ... + f(n-2)

    f(n) = f(0) + f(1) + f(2) + f(3) + ... + f(n-2) + f(n-1) = f(n-1) + f(n-1)

    可以得出:

    f(n) = 2*f(n-1)

7) 得出最終結論,在n階臺階,一次有1、2、...n階的跳的方式時,總得跳法為:

              | 1       ,(n=0 ) 

f(n) =     | 1       ,(n=1 )

              | 2*f(n-1),(n>=2)

也可以這樣更簡單:

                  每個臺階都有跳與不跳兩種情況(除了最後一個臺階),最後一個臺階必須跳。所以共用2^(n-1)中情況

######################################################################################

猛然的一次實現:

package com.mytest.mymain;


import java.io.*;


public class Main{
public static void main(String[] args) throws Exception{
               //方式1   #############
java.util.Scanner sc=new java.util.Scanner(System.in);//001
String str;
while((str=sc.nextLine())!=null){  //002
int n=Integer.parseInt(str);
long[] result=new long[n+2];
result[1]=1;
            result[2]=2;
            if(n==1 || n==2 ){
                System.out.println(result[n]);
            }else{
for(int i=3;i<=n;i++){
result[i]=result[i-1]+result[i-2];
} 
System.out.println(result[n]);
            }
}
      //方式2           #################
      /* BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
       String str;
while((str=br.readLine())!=null){
int n=Integer.parseInt(str);
long[] result=new long[n+2];
result[1]=1;
           result[2]=2;
           if(n==1 || n==2 ){
               System.out.println(result[n]);
           }else{
for(int i=3;i<=n;i++){
result[i]=result[i-1]+result[i-2];
} 
System.out.println(result[n]);
           }
}*/

}
}

線下兩個方式都可以通過的,但是在線上牛客網除錯方式2可以,方式1就不通過,方式一和方式二僅僅只是在001和002處兩處差異,其他程式碼完全一樣。

最終找到解決辦法:

把002改為:

//while(sc.hasNext()){  //003
//str=sc.nextLine();

所以在尋求統一形式編碼習慣的同時需要注意一些更多的細節區別。

 歡迎讀者思考,評論,發表自己觀點! 

##################################################

經典同原理題目,斐波那數列問題實現:

遞迴:f(n)=f(n-1)+f(n-2),n=0,f=0;n=1,f=1;

非遞迴高效實現:

1.時間複雜度為O(n)

public class Solution {
    public int Fibonacci(int n) {
        int f_1=1;
        int f_2=0;
        int result=0;
        
        if(n<=0){  return 0;   }
        if(n==1){   return 1;   }
        
        if(n>=2){
            for(int i=2;i<=n;i++){
                result=f_1+f_2;
                f_2=f_1;
                f_1=result; 
            }
        }
        
        return result;


    }
}


2.時間複雜度為O(logN)

利用如下數學公式:(一般要求非遞迴的動態規劃實現,如上實現即可,這種不夠實用,可做知識面儲備)

.