梯有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)
利用如下數學公式:(一般要求非遞迴的動態規劃實現,如上實現即可,這種不夠實用,可做知識面儲備)
.