NJU-高級算法-牛繁殖問題

Description

Cows in the FooLand city are interesting animals. One of their specialties is related to producing offsprings. A cow in FooLand produces its first calve (female calf) at the age of two years and proceeds to produce other calves (one female calf a year).

Now the farmer Harold wants to know how many animals would he have at the end of N years, if we assume that none of the calves die, given that initially, he has only one female calf?

explanation:At the end of 1 year, he will have only 1 cow, at the end of 2 years he will have 2 animals (one parent cow C1 and other baby calf B1 which is the offspring of cow C1).At the end of 3 years, he will have 3 animals (one parent cow C1 and 2 female calves B1 and B2, C1 is the parent of B1 and B2).At the end of 4 years, he will have 5 animals (one parent cow C1, 3 offsprings of C1 i.e. B1, B2, B3 and one offspring of B1).

Input

The first line contains a single integer T denoting the number of test cases. Each line of the test case contains a single integer N as described in the problem.

Output

For each test case print in new line the number of animals expected at the end of N years modulo 10^9 + 7.

Sample Input 1 

2
2
4

Sample Output 1

2
5

思路

題目大意:第0年有1只母牛A;第1年有1只母牛A;第2年2只母牛A,B(A生的);第3年3只母牛A,B,C(A生的);第4年5只母牛A,B,C,D(A生的),E(B生的)...

若記第N年末有F(N)只母牛,則有遞推關係F(N) = F(N-1) + F(N-2),其中F(N-1)表示前一年所有的牛(因爲牛不會死亡,所有牛都存活到今年),F(N-2)表示兩年前出生的牛今年開始具備繁殖能力,且每頭生1只牛。

可以看到這其實是一個斐波那契數列問題。

1.簡單的遞歸

n=0或n=1時,F(n) = 1

n>1時,F(n) = F(n-1) + F(n-2)

代碼:

import java.util.Scanner;

public class Main {
    
    /*
     *z遞歸法
     *z第一年末:一頭牛(A)
     *z第二年末:兩頭牛(A,B(A生了B))
     *z第三年末:三頭牛(A,B,C(A生了C))
     *z第四年末:五頭牛(A,B,C,D(A生了D),E(B生了E))
     *z第五年末:八頭牛(A,B,C,D,E,F(A生了F),G(B生了G),H(C生了H))
     *z...
     *z可以總結出結論:
     *T(N) = T(N - 1) + T(N - 2)
     *T(N - 1)表示前一年所有牛的數量,因爲第N年時前一年的所有牛都沒死
     *T(N - 2)表示第N年時兩年前新出生的牛今年開始具備繁殖能力,從而生出的新牛的數量
     * 
     * */
    private static int cal(int n) {
        if(n == 0) {
            return 1;
        } else if(n == 1) {
            return 1;
        } else {
            return cal(n - 1) + cal(n - 2);
        }
    }

	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);
		int cases = Integer.parseInt(sc.nextLine());
		for(int turn = 0; turn < cases; turn++) {
		    int n = Integer.parseInt(sc.nextLine());
		    int res = cal(n);
		    
		    System.out.println(res);
		}
		sc.close();
	}
}

但用遞歸超時,遞歸是時間複雜度爲O(n)的解法,斐波那契數列還存在一種O(logn)的解法

2.動態規劃

參考了:Fibonacci 數列O(logn)解法

遞歸慢的原因在於重複計算,導致有很多不必要的開銷。

採用動態規劃,所有值只需要進行一次計算。

斐波那契數列an(n ≥ 0),可以通過矩陣乘法的方式進行遞推:

進而可以得到:

而對於求冪運算,暴力累乘時間複雜度爲O(n),但可以通過遞歸進行優化:

a^n = (a^(n/2))^2 * a^(n%2)

這裏的n/2是取整,如5/2=2。這樣就可以實現相當於二分的效果,時間複雜度爲O(logn)。

代碼:

import java.util.Scanner;

public class Main {
    
    //兩個二維矩陣相乘
    private static long[][] multMartix2_2(long[][] m1, long[][] m2) {
        long[][] res = new long[2][2];
        res[0][0] = m1[0][0] * m2[0][0] + m1[0][1] * m2[1][0];
        res[0][1] = m1[0][0] * m2[0][1] + m1[0][1] * m2[1][1];
        res[1][0] = m1[1][0] * m2[0][0] + m1[1][1] * m2[1][0];
        res[1][1] = m1[1][0] * m2[0][1] + m1[1][1] * m2[1][1];
        
        return res;
    }
    
    //1行2列矩陣乘二維矩陣
    private static long[] multMatrix1_2(long[] m1, long[][] m2) {
        long res[] = new long[2];
        res[0] = m1[0] * m2[0][0] + m1[1] * m2[1][0];
        res[1] = m1[0] * m2[0][1] + m1[1] * m2[1][1];
        
        return res;
    }
    
    //二維矩陣的冪
    private static long[][] powMatrix2_2(long[][] m, long n){
        long[][] res = new long[2][2];
        if(n == 0) {
            res[0][0] = res[1][1] = 1;
            res[0][1] = res[1][0] = 0;
            return res;
        } else if(n == 1) {
            return m;
        } else {
            long[][] half = powMatrix2_2(m, n / 2);
            return multMartix2_2(multMartix2_2(half, half), powMatrix2_2(m, n % 2));
        }
    }
    
    private static long fib(long n) {
        if(n == 0) {
            return 1;
        }
        if(n == 1) {
            return 1;
        }
        //斐波那契數列的遞推矩陣爲{{0, 1}, {1, 1}}
        long[][] m = {{0, 1}, {1, 1}};
        //斐波那契數列初始兩值爲1
        long[] m0 = {1, 1};
        long[][] powRes = powMatrix2_2(m, n - 1);
        long[]res =  multMatrix1_2(m0, powRes);
        return res[1];
    }

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int cases = Integer.parseInt(sc.nextLine());
        for(int turn = 0; turn < cases; turn++) {
            long n = Long.parseLong(sc.nextLine());
            long res = fib(n);
            
            System.out.println(res);
        }
        sc.close();
    }
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章