【poj3070】矩陣乘法求斐波那契數列


   

【題目描述】

    我們知道斐波那契數列0 1 1 2 3 5 8 13……

    數列中的第i位為第i-1位和第i-2位的和(規定第0位為0,第一位為1)。

    求斐波那契數列中的第n位mod 10000的值。

 

【分析】

    這是我們熟悉的斐波那契數列,原來呢我們是遞推求值的嘛,當然這是最水的想法~~可是!這里的n很大誒,有10^9,for一遍肯定是不可以的咯。

    於是,我學會了用矩陣乘法求斐波那契數列(貌似是很經典的)。

    作為初學者的我覺得十分神奇!!

    好,我們來看:

    我們每次存兩個數f[i-1]和f[i-2],表示數列中的第i-1個數和第i-2的數,如何用這兩個數推出我們下一次要存的兩個數f[i]和f[i-1]呢,嗯,題目已經說得很清楚了:

        f[i-1]=f[i-1]

        f[i]=f[i-1]+f[i-2]

    可能你會覺得第一句有一點廢話,但是是有不一樣的意義的,這體現了遞推的過程,就是說我們每次掃兩個數,根據前面的兩個數推出后面的兩個數,這個思想在后面我做的一題——poj3734中有更好的體現。根據這個我們可以建一個2*2的矩陣A

1 1
1 0


    然后把我們每次存的兩個數放在另一個矩陣B里面:

f[i-1]
f[i-2]


    那么把這兩個矩陣相乘就可以得到另一個矩陣:

f[i]
f[i-1]

    這樣就可以得到f[i]了,至於為什么乘了之后會變成這兩個數,我們根據矩陣乘法的乘法規律可以很容易推出來。

這樣子的話,我們每次用A*B替換B,最后得到的矩陣的第一個數就是f[n]了。

 

    那么,矩陣乘法的優越性究竟體現在哪里呢。其實,矩陣乘法只是體現了我們從之前求的數到現在要求的數的遞推過程,就是說矩陣乘法可以完成多個元素的遞推。不過這個我們用普通的遞推就可以實現的啊~~認真想想我們就能發現,我們在矩陣乘法的過程中把上見面的A矩陣自己相乘了很多遍。就是說,我們可以求A矩陣的冪最后乘上B矩陣,既然要求冪,矩陣乘法滿足結合律,那么我們就可以用快速冪啦~~矩陣乘法的優越性就體現在這里:在遞推過程變成不斷乘以一個矩陣,然后用快速冪快速求得從第一個到第n個的遞推式,這樣子就可以在短時間內完成遞推了。

    哇塞,人類的智商啊~~讓我們繼續膜拜那些智商正無窮的大神吧,orz,orz……

 

    之前寫的快速冪都是遞歸式的,現在終於學會新的快速冪寫法,紀念一下:

 

1 while(n)
2 {
3     if(n&1) b=a*b;
4         a=a*a;
5     n>>=1;
6 }

    為什么這樣打快速冪可以呢,把指數換成二進制想一想,就可以發現了。

 

    接下來貼代碼,結構體真心好用,代碼好看多了~~嗯...另外做題目的時候要注意細節!!

 

 1 #include<cstdio>
 2 #include<cstdlib>
 3 #include<cstring>
 4 #define Mod 10000
 5 
 6 struct node
 7 {
 8     int v[3][3];
 9     int m,l;
10 };
11 
12 node get_mul(node a,node b)
13 {
14     node c;
15     c.m=a.m;c.l=b.l;
16     for(int i=1;i<=c.m;i++)
17       for(int j=1;j<=c.l;j++)
18       {
19           c.v[i][j]=0;
20           for(int k=1;k<=a.l;k++)
21               c.v[i][j]=(c.v[i][j]+a.v[i][k]*b.v[k][j])%Mod;
22       }
23     return c;
24 }
25 
26 int main()
27 {
28     freopen("a.in","r",stdin);
29     freopen("a.out","w",stdout);
30     int n;
31     while(1)
32     {
33         scanf("%d",&n);
34         if(n==0) {printf("0\n");continue;}
35         if(n==-1) break;
36         node a,b,c;
37         a.m=a.l=2,a.v[1][1]=1,a.v[1][2]=1,a.v[2][1]=1,a.v[2][2]=0;
38         b.m=b.l=2,b.v[1][1]=1,b.v[1][2]=0,b.v[2][1]=0,b.v[2][2]=1;
39         c.m=2,c.l=1,c.v[1][1]=1,c.v[2][1]=0;
40         n--;
41         while(n)
42         {
43             if(n&1) b=get_mul(a,b);
44             a=get_mul(a,a);
45             n>>=1;
46         }
47         b=get_mul(b,c);
48         printf("%d\n",b.v[1][1]);
49     }
50     return 0;
51 }
poj3070

   打表打得好惡心啊!!

    

2015-09-19 10:33:36

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM