1. 程式人生 > >生成函數(母函數)入門詳解

生成函數(母函數)入門詳解

參考 nsh 意義 數值 tar 得到 再次 fin 表達式

本文章從以上兩位大佬的博客參考而來!再次感謝!

母函數,又稱生成函數,是ACM競賽中經常使用的一種解題算法,常用來解決組合方面的題目。

在數學中,某個序列的母函數(Generating function,又稱生成函數)是一種形式冪級數,其每一項的系數可以提供

關於這個序列的信息。使用母函數解決問題的方法稱為母函數方法。

母函數可分為很多種,包括普通母函數、指數母函數、L級數、貝爾級數和狄利克雷級數。對每個序列都可以寫出以

上每個類型的一個母函數。構造母函數的目的一般是為了解決某個特定的問題,因此選用何種母函數視乎序列本身的

特性和問題的類型。

這裏先給出兩句話

1.“把組合問題的加法法則和冪級數的乘冪對應起來”

2.“母函數的思想很簡單 — 就是把離散數列和冪級數一 一對應起來,把離散數列間的相互結合關系對應成為冪級數間的

運算關系,最後由冪級數形式來確定離散數列的構造. “

我們首先來看下這個多項式乘法:

技術分享圖片

母函數圖(1)

由此可以看出:

1.x的系數是a1,a2,…an 的單個組合的全體。

2. x^2的系數是a1,a2,…a2的兩個組合的全體。

………

n. x^n的系數是a1,a2,….an的n個組合的全體(只有1個)。

進一步得到:

技術分享圖片

母函數圖(2)

母函數的定義

對於序列a0,a1,a2,…構造一函數:

技術分享圖片

母函數圖(3)

稱函數G(x)是序列a0,a1,a2,…的母函數。

第一種:

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

考慮用母函數來解決這個問題:

我們假設x表示砝碼,x的指數表示砝碼的重量,這樣:

1個1克的砝碼可以用函數1+1*x^1表示,

1個2克的砝碼可以用函數1+1*x^2表示,

1個3克的砝碼可以用函數1+1*x^3表示,

1個4克的砝碼可以用函數1+1*x^4表示,

上面這四個式子懂嗎?

我們拿1+x^2來說,前面已經說過,x表示砝碼,x的指數表示砝碼的重量!初始狀態時,這裏就是一個質量為2的砝碼。

那麽前面的1表示什麽?按照上面的理解,1其實應該寫為:1*x^0,即1代表重量為2的砝碼數量為0個。

所以這裏1+1*x^2 = 1*x^0 + 1*x^2,即表示2克的砝碼有兩種狀態,不取或取,不取則為1*x^0,取則為1*x^2

不知道大家理解沒,我們這裏結合前面那句話:

“把組合問題的加法法則和冪級數的乘冪對應起來“

接著討論上面的1+x^2,這裏x前面的系數有什麽意義?

這裏的系數表示狀態數(方案數)

1+x^2,也就是1*x^0 + 1*x^2,也就是上面說的不取2克砝碼,此時有1種狀態;或者取2克砝碼,此時也有1種狀態。(分析!)

所以,前面說的那句話的意義大家可以理解了吧?

幾種砝碼的組合可以稱重的情況,可以用以上幾個函數的乘積表示:

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

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

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

從上面的函數知道:可稱出從1克到10克,系數便是方案數。(!!!經典!!!)

例如右端有2^x^5 項,即稱出5克的方案有2種:5=3+2=4+1;同樣,6=1+2+3=4+2;10=1+2+3+4。

故稱出6克的方案數有2種,稱出10克的方案數有1種 。


接著上面,接下來是第二種情況:

第二種:

求用1分、2分、3分的郵票貼出不同數值的方案數:

大家把這種情況和第一種比較有何區別?第一種每種是一個,而這裏每種是無限的。

技術分享圖片

母函數圖(4)

以展開後的x^4為例,其系數為4,即4拆分成1、2、3之和的拆分方案數為4;

即 :4=1+1+1+1=1+1+2=1+3=2+2

母函數通常解決類似如下的問題:

給5張1元,4張2元,3張5元,要得到15元,有多少種組合?

某些時候會規定至少使用3張1元、1張2元、0張5元。

某些時候會規定有無數張1元、2元、5元。

……

解題過程

解題時,首先要寫出表達式,通常是多項的乘積,每項由多個x^y組成。如(1+x+x^2)(1+x^4+x^8)(x^5+x^10+x^15)。

通用表達式為

(x^(v[0]*n1[0])+x^(v[0]*(n1[0]+1))+x^(v[0]*(n1[0]+2))+...+x^(v[0]*n2[0]))
(x^(v[1]*n1[1])+x^(v[1]*(n1[1]+1))+x^(v[1]*(n1[1]+2))+...+x^(v[1]*n2[1]))
...
(x^(v[K]*n1[K])+x^(v[K]*(n1[K]+1))+x^(v[K]*(n1[K]+2))+...+x^(v[K]*n2[K]))

K對應具體問題中物品的種類數。

v[i]表示該乘積表達式第i個因子的權重,對應於具體問題的每個物品的價值或者權重。

n1[i]表示該乘積表達式第i個因子的起始系數,對應於具體問題中的每個物品的最少個數,即最少要取多少個。

n2[i]表示該乘積表達式第i個因子的終止系數,對應於具體問題中的每個物品的最多個數,即最多要取多少個。

對於表達式(1+x+x^2)(x^8+x^10)(x^5+x^10+x^15+x^20),v[3]={1,2,5},n1[3]={0,4,1},n2[3]={2,5,4}。

解題的關鍵是要確定v、n1、n2數組的值。

通常n1都為0,但有時候不是這樣。

n2有時候是無限大。

通用模板:

 1 #include<stdio.h>
 2 #include<string.h>
 3 #include<algorithm>
 4 #include<stack>
 5 #include<queue>
 6 #include<iostream>
 7 #include<map>
 8 #include<vector>
 9 #define Inf 0x3f3f3f3f
10 #define PI acos(-1.0)
11 using namespace std;
12 const int MXAN=300+10;
13 int dp[1234];
14 int str[1234];
15 int main()
16 {
17     int m,n,i,j,pos;
18     int a[MXAN];
19     int b[MXAN];
20     while(cin>>n&&n)
21     {
22         for(int i=0;i<=300;i++)
23         {
24             a[i]=1;
25             b[i]=0;
26         }
27         for(int i=2;i<=17;i++)
28         {
29             for(int j=0;j<=n;j++)
30             {
31                 for(int k=0;k+j<=n;k+=i*i)
32                 {
33                     b[k+j]+=a[j];
34                 }
35             }
36             for(int j=0;j<=n;j++)
37             {
38                 a[j]=b[j];
39                 b[j]=0;
40             }
41         }
42         cout<<a[n]<<endl;
43     }
44     return 0;
45 }

我們來解釋下上面標誌的各個地方:(***********!!!重點!!!***********)

① 、首先對c1初始化,由第一個表達式(1+x+x^2+..x^n)初始化,把質量從0到n的所有砝碼都初始化為1.

② 、 i從2到n遍歷,這裏i就是指第i個表達式,上面給出的第二種母函數關系式裏,每一個括號括起來的就是一個表達式。

③、j 從0到n遍歷,這裏j就是(前面幾個表達式累乘的表達式)裏第j個變量,如(1+x)(1+x^2)(1+x^3),j先指示的是1和x的系數,i=2執行完之後變為

(1+x+x^2+x^3)(1+x^3),這時候j應該指示的是合並後的第一個括號的四個變量的系數。

④ 、 k表示的是第j個指數,所以k每次增i(因為第i個表達式的增量是i)。

⑤ 、把c2的值賦給c1,而把c2初始化為0,因為c2每次是從一個表達式中開始的。

生成函數(母函數)入門詳解