斐波那契數列(Fibonacci sequence),又稱黃金分割數列、因數學家列昂納多·斐波那契(Leonardoda Fibonacci)以兔子繁殖爲例子而引入,故又稱爲「兔子數列」,指的是這樣一個數列:一、一、二、三、五、八、1三、2一、3四、……在數學上,斐波納契數列以以下被以遞推的方法定義:F(1)=1,F(2)=1, F(n)=F(n-1)+F(n-2)(n>=3,n∈N*)在現代物理、準晶體結構、化學等領域,斐波納契數列都有直接的應用html
兔子生兔子問題,兔子數列。java
古典問題:有一對兔子,從出生後第3個月起每月都生一對兔子,小兔子長到第四個月後每月又生一對兔子,假如兔子都不死,問每月的兔子總數爲多少git
兔子的規律爲數列1,1,2,3,5,8,13,21....github
package com.cnblogs.mufasa.Fibonacci; public class Answer1_Rabite { //最普通的遞歸調用,會作出不少次重複計算 public static int solution1(int x){ if(x==1 || x==2){ return 1; }else { return solution1(x-1)+solution1(x-2); } } //動態規劃,速度很快【逆推導】 public static int solution2(int x){ if(x==1 || x==2){ return 1; } int a=1,b=1,c=0; for(int i=3;i<=x;i++){ c=a+b; a=b; b=c; } return c; } }
有 N 階樓梯,每次能夠上一階或者兩階,求有多少種上樓梯的方法。算法
package com.cnblogs.mufasa.Fibonacci; public class Answer2_climbStairs { public static int solution1(int x){//遞歸解法 if(x<=2){ return x; } return solution1(x-1)+solution1(x-2); } public static int solution2(int x){//動態規劃 if(x<=2){ return x; } int a=1,b=2,c=0;//與生兔子問題不一樣,這裏起始有變化 for(int i=3;i<=x;i++){ c=a+b; a=b; b=c; } return c; } }
https://leetcode-cn.com/problems/house-robber-ii/數組
搶劫一排住戶,可是不能搶鄰近的住戶,求最大搶劫量ide
values={1,2,3,11,12,3,1,12};spa
package com.cnblogs.mufasa.Fibonacci; public class Answer3_Robber { //最普通的遞歸調用,會作出不少次重複計算 public static int solution1(int[] values,int index){ int x=values.length; if(x-index==1){ return values[index]; }else if(x-index==2) { return (values[index] > values[index-1] ? values[index] : values[index-1]); } int a=solution1(values,index+1);//不搶這一家 int b=values[index]+solution1(values,index+2);//搶這一家 return (a>b?a:b); } //動態規劃,速度很快【逆推導】 public static int solution2(int[] values){ int x=values.length; if(x==1){ return values[0]; }else if(x==2) { return (values[0] > values[1] ? values[0] : values[1]); } int a=values[0],b=(values[0]>values[1]?values[0]:values[1]),c=0; for(int i=2;i<x;i++){ c=(a+values[i]>b?a+values[i]:b); a=b; b=c; } return c; } }
https://leetcode.com/problems/house-robber-ii/description/.net
你是一個專業的小偷,計劃偷竊沿街的房屋,每間房內都藏有必定的現金。這個地方全部的房屋都圍成一圈,這意味着第一個房屋和最後一個房屋是緊挨着的。同時,相鄰的房屋裝有相互連通的防盜系統,若是兩間相鄰的房屋在同一夜被小偷闖入,系統會自動報警。3d
給定一個表明每一個房屋存放金額的非負整數數組,計算你在不觸動警報裝置的狀況下,可以偷竊到的最高金額。
package com.cnblogs.mufasa.Fibonacci; public class Answer4_Robber2 { //最普通的遞歸調用,會作出不少次重複計算 public static int solution1(int[] values,int index0,int index1){//起始位置、終止 int x=values.length; if(x-index1-index0==1){ return values[index0]; }else if(x-index1-index0==2) { return (values[index0] > values[index0-1] ? values[index0] : values[index0-1]); } int a=solution1(values,index0+1,index1);//不搶這一家 int b=values[index0]+solution1(values,index0+2,index1);//搶這一家 return (a>b?a:b); } //動態規劃,速度很快【逆推導】 public static int solution2(int[] values){ if(values==null || values.length==0){ return 0; } int x=values.length; if(x==1){ return values[0]; } int a=singleDeal(values,0,1); int b=singleDeal(values,1,0); return (a>b?a:b); } private static int singleDeal(int[] values,int index0,int index1){// int x=values.length; if(x-index1-index0==1){ return values[index0]; }else if(x-index1-index0==2) { return (values[index0] > values[index0+1] ? values[index0] : values[index0+1]); } int a=values[index0],b=(values[index0]>values[index0+1]?values[index0]:values[index0+1]),c=0; for(int i=index0+2;i<x-index1;i++){ c=(a+values[i]>b?a+values[i]:b); a=b; b=c; } return c; } }
http://www.javashuo.com/article/p-ebaqvsjx-bb.html
有 N 個 信 和 信封,它們被打亂,有多少種狀況是全部人都收到了錯誤的郵件?
當n個編號元素放在n個編號位置,元素編號與位置編號各不對應的方法數用dp[n]表示,那麼dp[n-1]就表示n-1個編號元素放在n-1個編號位置,各不對應的方法數,其它類推.
第一步,把第n個元素放在一個位置,好比位置k,一共有n-1種方法;
第二步,放編號爲k的元素,這時有兩種狀況:⑴把它放到位置n,那麼,對於剩下的n-1個元素,因爲第k個元素放到了位置n,剩下n-2個元素就有dp[n-2]種方法;⑵第k個元素不把它放到位置n,這時,對於這n-1個元素,有dp[n-1]種方法;
package com.cnblogs.mufasa.Fibonacci; public class Answer5_mail { //最普通的遞歸調用,會作出不少次重複計算 public static int solution1(int x){ if(x<=1){ return 0; }else if(x==2){ return 1; } return (x-1)*(solution1(x-1)+solution1(x-2)); } //動態規劃,速度很快【逆推導】 public static int solution2(int x){ if(x<=1){ return 0; }else if(x==2){ return 1; } int a=0,b=1,c=0; for(int i=3;i<=x;i++){ c=(i-1)*(a+b); a=b; b=c; } return c; } }
題目描述:假設農場中成熟的母牛每一年都會生 1 頭小母牛,而且永遠不會死。第一年有 1 只小母牛,從第二年開始,母牛開始生小母牛。每隻小母牛 3 年以後成熟又能夠生小母牛。給定整數 N,求 N 年後牛的數量。
package com.cnblogs.mufasa.Fibonacci; public class Answer6_cow { //最普通的遞歸調用,會作出不少次重複計算 public static int solution1(int x){ if(x<=4){ return x; }else { return solution1(x-1)+solution1(x-3); } } //動態規劃,速度很快【逆推導】 public static int solution2(int x){ if(x<=4){ return x; } int a=2,b=3,c=4,d=0; for(int i=5;i<=x;i++){ d=a+c; a=b; b=c; c=d; } return d; } }
驗證程序:
package com.cnblogs.mufasa.Fibonacci; public class Client { public static void main(String[] args) { // System.out.println(Answer1_Rabite.solution1(8));//兔子問題,遞歸解法 // System.out.println(Answer1_Rabite.solution2(8));//兔子問題,動態規劃 // System.out.println(Answer2_climbStairs.solution1(5));//爬樓梯問題,遞歸解法 // System.out.println(Answer2_climbStairs.solution2(5));//爬樓梯問題,動態規劃 // System.out.println(Answer3_Robber.solution1(new int[]{1,2,3,11,12,3,1,12},0));//強盜問題,遞歸解法 // System.out.println(Answer3_Robber.solution2(new int[]{1,2,3,11,12,3,1,12}));//強盜問題,動態規劃 // int a=Answer4_Robber2.solution1(new int[]{1,2,3,11,12,3,1,12},0,1);//環形強盜問題,遞歸解法 // int b=Answer4_Robber2.solution1(new int[]{1,2,3,11,12,3,1,12},1,0); // System.out.println((a>b?a:b));//環形強盜問題,遞歸解法 // System.out.println(Answer4_Robber2.solution2(new int[]{1,2,3,11,12,3,1,12}));//環形強盜問題,動態規劃 // System.out.println(Answer5_mail.solution1(5));//信件問題,遞歸解法 // System.out.println(Answer5_mail.solution2(5));//信件問題,動態規劃 // System.out.println(Answer6_cow.solution1(8));//母牛問題,遞歸解法 // System.out.println(Answer6_cow.solution2(8));//母牛問題,動態規劃 } }
https://leetcode-cn.com/problems/minimum-path-sum/
給定一個包含非負整數的 m x n 網格,請找出一條從左上角到右下角的路徑,使得路徑上的數字總和爲最小。
package com.cnblogs.mufasa.Matrix_path; public class Answer1_minPath { public static int Solution1(int[][] grid) {//遞歸調用,算法複雜度O(2^(m+n)),空間複雜度爲O(m+n) return calculate(grid, 0, 0); } private static int calculate(int[][] grid, int i, int j){ if (i == grid.length || j == grid[0].length) return Integer.MAX_VALUE;//沒法到達此位置 if (i == grid.length - 1 && j == grid[0].length - 1) return grid[i][j];//此位置爲目的地 return grid[i][j] + Math.min(calculate(grid, i + 1, j), calculate(grid, i, j + 1));//相似於二叉樹分裂 } public static int Solution2(int[][] grid) {//動態規劃,二維 if(grid.length==0||grid[0].length==0){ return 0; } int m=grid.length,n=grid[0].length; int[][] dp=new int[m][n]; for (int i = m - 1; i >= 0; i--) { for (int j = n - 1; j >= 0; j--) { if (i == grid.length - 1 && j != grid[0].length-1) { dp[i][j] = grid[i][j] + dp[i][j + 1]; } else if (i != grid.length - 1 && j == grid[0].length-1) { dp[i][j] = grid[i][j] + dp[i + 1][j]; } else if (i != grid.length - 1 && j != grid[0].length-1) { dp[i][j] = grid[i][j] + Math.min(dp[i + 1][j], dp[i][j + 1]); } else { dp[i][j] = grid[i][j]; } } } return dp[0][0]; } public static int Solution3(int[][] grid) {//一維動態規劃 if (grid.length == 0 || grid[0].length == 0) { return 0; } int m = grid.length, n = grid[0].length; int[] dp = new int[n]; for(int i = m-1; i >=0; i--){ for(int j = n-1; j >=0; j--){ if(i==m-1&&j!=n-1){//水平移動 dp[j]=grid[i][j]+dp[j+1]; }else if(i!=m-1&&j==n-1){//上下移動 dp[j]=grid[i][j]+dp[j]; }else if(i!=m-1&&j!=n-1){ dp[j]=grid[i][j]+Math.min(dp[j],dp[j+1]); }else { dp[j]=grid[i][j]; } } } return dp[0]; } public static int Solution4(int[][] grid) {//動態規劃,本質與上一個一維動態規劃一致 if (grid.length == 0 || grid[0].length == 0) { return 0; } int m = grid.length, n = grid[0].length; int[] dp = new int[n]; for (int i = 0; i < m; i++) { for (int j = 0; j < n; j++) { if (j == 0) { dp[j] = dp[j]; // 只能從上側走到該位置 } else if (i == 0) { dp[j] = dp[j - 1]; // 只能從左側走到該位置 } else { dp[j] = Math.min(dp[j - 1], dp[j]); } dp[j] += grid[i][j]; } } return dp[n - 1]; } public static int Solution5(int[][] grid) {//動態規劃,不開闢新空間,可是會修改元數組內容,本質仍是同樣 if (grid.length == 0 || grid[0].length == 0) { return 0; } int m = grid.length, n = grid[0].length; for (int i = 0; i < m; i++) { for (int j = 0; j < n; j++) { if(i==0&&j!=0){ grid[i][j]=grid[i][j-1]+grid[i][j]; }else if(i!=0&&j==0){ grid[i][j]=grid[i-1][j]+grid[i][j]; }else if(i!=0&&j!=0){ grid[i][j]=grid[i][j]+Math.min(grid[i-1][j],grid[i][j-1]); } } } return grid[m-1][n-1]; } }
一個機器人位於一個 m x n 網格的左上角 (起始點在下圖中標記爲「Start」 )。
機器人每次只能向下或者向右移動一步。機器人試圖達到網格的右下角(在下圖中標記爲「Finish」)。
問總共有多少條不一樣的路徑?
package com.cnblogs.mufasa.Matrix_path; import java.util.Arrays; public class Answer2_multiple { public static int solution1(int m, int n){//遞歸調用 if(m==1&&n==1){//原地不動 return 1; } if(m>1&&n>1){ return solution1(m-1, n)+solution1(m, n-1); }else { return 1; } } public static int solution2(int m, int n){//動態規劃,一維 int[] dp = new int[n]; Arrays.fill(dp,1); for (int i = 1; i < m; i++) { for (int j = 1; j < n; j++) { dp[j] = dp[j] + dp[j - 1]; } } return dp[n-1]; } }
驗證程序:
package com.cnblogs.mufasa.Matrix_path; public class Client { public static void main(String[] args) { // int[][] grid={{1,3,4,8},{3,2,2,4},{5,7,1,9},{2,3,2,3},{2,3,2,3}}; // System.out.println(Answer1_minPath.Solution1(grid));//遞歸調用 // System.out.println(Answer1_minPath.Solution2(grid));//二維動態規劃 // System.out.println(Answer1_minPath.Solution3(grid));//一維動態規劃 // System.out.println(Answer1_minPath.Solution4(grid));//一維動態規劃 // System.out.println(Answer1_minPath.Solution5(grid));//動態規劃,不開闢空間 System.out.println(Answer2_multiple.solution1(3,7)); System.out.println(Answer2_multiple.solution2(3,7)); } }
①時間-任務-價值問題:https://blog.csdn.net/houboTech/article/details/79689157
視頻講解:https://www.bilibili.com/video/av16544031?from=search&seid=4083931774448408088
這裏使用三種解題思路:①遞歸算法;②登記表【overlap sub-problem重疊子問題】;③動態規劃。
package com.cnblogs.mufasa.demo1; import java.util.HashMap; public class Time_value { private final int num=8; private final int[][] times=new int[][] {{1,4},{3,5},{0,6},{4,7},{3,8},{5,9},{6,10},{8,11}}; private final int[] values={5,1,8,4,6,3,2,4}; private final int[] prevs=preNode(times); //遞歸法,時間複雜度爲O(2^n) public int rec_opt(int i) { if(i<=0) return 0; if(i==1) return values[i-1]; int choice = values[i-1] + rec_opt(prevs[i-1]); int notChoice = rec_opt(i-1); return max(notChoice,choice); } //登記表形式,算法複雜度(n)【本質是將重複子問題進行記錄防止重複計算】 HashMap<Integer,Integer> hm=new HashMap<>(50); public int dict_opt(int i){ if(hm.containsKey(i)){ return hm.get(i); } int outNum=0; if(i==1){ outNum= values[i-1]; }else { outNum=max(values[i-1] + rec_opt(prevs[i-1]),rec_opt(i-1)); } hm.put(i,outNum); return outNum; } //動態規劃,時間複雜度爲O(n),本質上與登記表形式相同,可是代碼更加優雅 public int dp_opt() {//求作任務能夠獲得的最大價值(動態規劃法) int[] opt = new int[values.length + 1]; opt[0] = 0; opt[1] = values[0]; for (int i = 1; i < opt.length; i++) { int choice = values[i - 1] + opt[prevs[i - 1]]; int notChoice = opt[i - 1]; opt[i] = max(choice, notChoice); } return opt[opt.length - 1]; } //輔助方法 private int[] preNode(int[][] times){//獲取prevs數據【成功】 int[] prevs=new int[times.length]; for(int i=1;i<times.length;i++){ for(int j=i-1;j>=0;j--){ if(times[j][1]<=times[i][0]){ prevs[i]=j+1; break; } } } return prevs; } private static int max(int a,int b){ return (a>=b?a:b); } //算法驗證 public static void main(String[] args) { Time_value tv=new Time_value(); int result1=tv.rec_opt(8); int result2=tv.dp_opt(); int result3=tv.dict_opt(8); System.out.println(result1); System.out.println(result2); System.out.println(result3); } }
參考連接: