ACM數論之旅16---母函數(又名生成函數)(痛並快樂着(╭ ̄3 ̄)╭)

 

(前排出售零食瓜子)php

前言:算法

母函數是個很難的東西,難在數學ide

而ACM中所用的母函數只是母函數的基礎函數

應該說除了很差理解外,其餘都是很是簡單的工具

 

 

 

 

 

 

 

 

 

母函數生成函數,是組合數學中尤爲是計數方面的一個重要理論和工具。spa

可是ACM中的母函數木有像數學那麼深究,應用的都是母函數的一些基本3d

(就比如方程的配方,因式的分解,寫起來容易,你用電腦寫起來就麻煩了,因此學計算機就不要老跟數學家瞎鬧( ̄3 ̄))code

 

 

什麼是母函數blog

就是把一個已知的序列和x的多項式合併起來,新產生的多項式就叫原來序列的母函數ci

 

至於怎麼合併,看這個例子

 

序列{0,1,2,3,4,5...n}的母函數就是

f(x)=0+x+2x^2+3x^3+4x^4+...+nx^n(這個x沒有任何意義,應該說,你不須要把它當作一個函數,你只要知道母函數這麼寫就能夠了)

 

序列{1,1,1,1,1......}的母函數就是

f(x)=1+x+x^2+x^3+x^4....

 

二項式展開的序列好比這個{1,4,6,4,1,0,0,0,0,0.....}是C(4,0)到C(4,4)的係數,那它的母函數就是

f(x)=1+4x+6x^2+4x^3+1x^4

 

母函數就長這樣,對正常人來說,這種東西毫無心義( ° △ °|||)

 

 

 

 

 

 

 

 

 

 

 

那看點有意義的東西(如下都是經典題型,我從杭電ACM課件抄來的)

 

有1克、2克、3克、4克的砝碼各一枚,能稱出哪幾種重量?每種重量各有幾種可能方案?

假如x的冪次數表示幾克的砝碼

那麼

1克的砝碼錶示爲1+x^1

2克的砝碼錶示爲1+x^2

3克的砝碼錶示爲1+x^3

4克的砝碼錶示爲1+x^4

 

每一個砝碼均可以選擇取或不取

因此這裏的1能夠認爲1*x^0,表示不取這顆砝碼

 

那麼把這些乘起來

(1+x^1)(1+x^2)(1+x^3)(1+x^4)

=1+(x^1)+(x^2)+2(x^3)+2(x^4)+2(x^5)+2(x^6)+2(x^7)+(x^8)+(x^9)+(x^10)

 

根據指數來看,咱們能夠稱出0~10這麼多的重量,其中3~7的係數爲2,說明有2種稱的方法

那麼咱們來細看一遍

0:(什麼砝碼都不放).......................(1種)

1:1.............................................(1種)

2:2.............................................(1種)

3:3或1+2.....................................(2種)

4:4或1+3.....................................(2種)

5:1+4或2+3.................................(2種)

6:2+4或1+2+3..............................(2種)

7:3+4或1+2+4..............................(2種)

8:1+3+4......................................(1種)

9:2+3+4......................................(1種)

10:1+2+3+4.................................(1種)

 

分絕不差(・ˍ・*)

因此說母函數在ACM就是這麼用的,跟函數不要緊,跟寫法有關係。。。

 

 

 

 

 

再來一題

求用1分、2分、3分的郵票貼出不一樣數值的方案數:(每張郵票的數量是無限的)

那麼

1分:(1+x^1+x^2+x^3+x^4+......)

2分:(1+x^2+x^4+x^6+x^8+......)

3分:(1+x^3+x^6+x^9+x^12+......)

而後這3個乘起來(讓電腦去乘吧)

 

對於這種無限的,題目確定會給你他詢問的數值的範圍,計算到最大的範圍就能夠了

 

 

附代碼:

 1 #include<cstdio>
 2 typedef long long LL;
 3 const int N = 100 + 5;//假如題目只問到100爲止 
 4 const int MAX = 3;//題目只有1,2,3這3種郵票 
 5 LL c1[N], c2[N];//c2是臨時合併的多項式,c1是最終合併的多項式 
 6 int n;
 7 void init(){
 8     c1[0] = 1;//一開始0的狀況算一種 
 9     for(int i = 1; i <= MAX; i ++){//把1分到MAXN的郵票合併,變成一個多項式 
10         for(int j = 0; j < N; j += i){//i分的郵票,步長是i
11             for(int k = 0; j + k < N; k ++){//從x^0到x^N遍歷一遍 
12                 c2[j + k] += c1[k];//由於j的全部項係數爲1,因此c1[k]能夠當作c1[k]*1; 
13             }
14         } 
15         for(int j = 0; j < N; j ++){//把c2的數據抄到c1,清空c2 
16             c1[j] = c2[j];
17             c2[j] = 0;
18         }
19     }
20 } 
21 int main(){
22     init();
23     while(scanf("%d", &n) != EOF){
24         printf("%I64d\n", c1[n]);
25     }
26 }

 

 

 

 

 

 

咱們就來把這個模板用於實際吧

hdu 1028

http://acm.hdu.edu.cn/showproblem.php?pid=1028

題目問一個數字n可以拆成多少種數字的和

好比n=4

  4 = 4;
  4 = 3 + 1;
  4 = 2 + 2;
  4 = 2 + 1 + 1;
  4 = 1 + 1 + 1 + 1;

有5種,那麼答案就是5

 

AC代碼:

 1 #include<cstdio>
 2 typedef long long LL;
 3 const int N = 120 + 5;
 4 const int MAX = 120 + 5;
 5 LL c1[N], c2[N];
 6 int n;
 7 void init(){
 8     c1[0] = 1;
 9     for(int i = 1; i <= MAX; i ++){
10         for(int j = 0; j < N; j += i){
11             for(int k = 0; j + k < N; k ++){
12                 c2[j + k] += c1[k];
13             }
14         } 
15         for(int j = 0; j < N; j ++){
16             c1[j] = c2[j];
17             c2[j] = 0;
18         }
19     }
20 } 
21 int main(){
22     init();
23     while(scanf("%d", &n) != EOF){
24         printf("%I64d\n", c1[n]);
25     }
26 }
View Code

 

 

 

再來,hdu 1398

http://acm.hdu.edu.cn/showproblem.php?pid=1398

題目說一個國家的硬幣都是方形的,面值也是方形的

有1塊錢,4塊錢,9塊錢,16塊錢......一直到289塊錢(17^2)

問想組成n塊錢有幾種方法

 

AC代碼:

 1 #include<cstdio>
 2 typedef long long LL;
 3 const int N = 300 + 5;
 4 const int MAX = 17;
 5 LL c1[N], c2[N];
 6 int n;
 7 void init(){
 8     c1[0] = 1;
 9     for(int i = 1; i <= MAX; i ++){
10         for(int j = 0; j < N; j += i*i){
11             for(int k = 0; j + k < N; k ++){
12                 c2[j + k] += c1[k];
13             }
14         } 
15         for(int j = 0; j < N; j ++){
16             c1[j] = c2[j];
17             c2[j] = 0;
18         }
19     }
20 } 
21 int main(){
22     init();
23     while(scanf("%d", &n) != EOF && n){
24         printf("%I64d\n", c1[n]);
25     }
26 }
View Code

 

 

都是改一些小地方,都是模板題(o゚ω゚o)

 

 

最後一道

hdu 1085

http://acm.hdu.edu.cn/showproblem.php?pid=1085

 

AC代碼:

 1 #include<cstdio>
 2 #include<cstring>
 3 typedef long long LL;
 4 const int N = 1000 * (1+2+5) + 5;
 5 int cost[3] = {1, 2, 5};
 6 LL c1[N], c2[N];
 7 int num[3];
 8 int MAX;
 9 int main(){
10     while(~scanf("%d%d%d", &num[0], &num[1], &num[2])){
11         if(num[0] == 0 && num[1] == 0 && num[2] == 0) break;
12         memset(c1, 0, sizeof(c1));
13         memset(c2, 0, sizeof(c2));
14         MAX = num[0] + num[1] * 2 + num[2] * 5;//計算最大值
15         c1[0] = 1;
16         for(int i = 0; i < 3; i ++){
17             for(int j = 0; j <= num[i] * cost[i]; j += cost[i]){
18                 for(int k = 0; j + k <= MAX; k ++){
19                     c2[j + k] += c1[k];
20                 }
21             } 
22             for(int j = 0; j < N; j ++){
23                 c1[j] = c2[j];
24                 c2[j] = 0;
25             }
26         }
27         for(int i = 1; i <= MAX + 1; i ++){
28             if(!c1[i]){
29                 printf("%d\n", i);
30                 break;
31             }
32         }
33     }
34 } 
View Code

 

 

 

 

 

 

 

 

 

 

母函數在數學上真的用處很大,可是我沒怎麼看到在ACM上有什麼太大的用處(可能我作的題還不夠多 T_T)

好比剛剛上面的3個例題,都有更快的作法

第一題:動態規劃,時間複雜度O(n^2)

 1 #include<cstdio>
 2 const int N = 120 + 5;
 3 int dp[N];
 4 int n;
 5 void init(){
 6     dp[0] = 1;
 7     for(int i = 1; i < N; i ++){
 8         for(int j = i; j < N; j ++){
 9             dp[j] += dp[j - i];
10         }
11     }
12 }
13 int main(){
14     init();
15     while(scanf("%d", &n) != EOF){
16         printf("%d\n", dp[n]);
17     }
18 }
View Code

 

第二題:動態規劃,時間複雜度O(n^2)

 1 #include<cstdio>
 2 const int N = 300 + 5;
 3 int dp[N];
 4 int n;
 5 void init(){
 6     dp[0] = 1;
 7     for(int i = 1; i <= 17; i ++){
 8         for(int j = i*i; j < N; j ++){
 9             dp[j] += dp[j - i*i];
10         }
11     }
12 }
13 int main(){
14     init();
15     while(scanf("%d", &n) != EOF && n){
16         printf("%d\n", dp[n]);
17     }
18 }
View Code

 

第三題:≖‿≖✧特判就行了,時間複雜度O(1)

 1 #include<cstdio>
 2 int a, b, c;
 3 int ans;
 4 int main(){
 5     while(~scanf("%d%d%d", &a, &b, &c) && (a || b || c)){
 6         if(a >= 4 || a >= 2 && b >= 1 || a >= 1 && b >= 2) ans = a + 2*b + 5*c + 1;
 7         else if(a == 0) ans = 1;
 8         else ans = a + 2*b + 1;
 9         printf("%d\n", ans);
10     }
11 }
View Code

 

哈哈哈有沒有被騙的感受,有些題目,不要陷進算法裏,這題O(1)的複雜度就能夠了,若是你用三個for循環,那就太慢了,並且數量不一樣,尚未辦法預處理,若是數據量大,確定超時

 

 

 

 

 

因此,母函數咱們只要理解原理就行了

 

 

 

 

 

 

 

 

 

 

 

 

 

那麼ACM的母函數講完了(*°∀°)

以後是數學上的母函數,不想看的人就能夠結束本章內容了(*°∀°)

 

 

 

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
哇哈哈╰(*°▽°*)╯我也不想講了
本身百度去吧╰(*°▽°*)╯
聽說能夠求不少東西
好比Fibonacci(斐波那契)數列的通項公式
Catalan數(卡塔蘭數)的通項公式也是母函數求得的
還有"牛頓二項式定理"等等。。。
想了解本身百度╰(*°▽°*)╯
相關文章
相關標籤/搜索