母函式詳解和史上最通用最高效的母函式模板
版權所有。所有權利保留。
歡迎轉載,轉載時請註明出處:
http://blog.csdn.net/xiaofei_it/article/details/17042651
母函式,又稱生成函式,是ACM競賽中經常使用的一種解題演算法,常用來解決組合方面的題目。
本文講解母函式,但不講解該演算法的基礎理論。讀者隨便找一本組合數學教材便可找到相應的內容,或者直接在網上搜尋一下。
母函式通常解決類似如下的問題:
給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有時候是無限大。
之後就實現表示式相乘,從第一個因子開始乘,直到最後一個為止。此處通常使用一個迴圈,迴圈變數為i。每次迭代的計算結果放在陣列a中。計算結束後,a[i]表示權重i的組合數,對應具體問題的組合數。
迴圈內部是把每個因子的每個項和a中的每個項相乘,加到一個臨時的陣列b的對應位(這裡有兩層迴圈,加上最外層迴圈,總共有三層迴圈),之後就把b賦給a。
這些過程通常直接套用模板即可。
通用模板
下面我直接給出通用模板:
//為計算結果,b為中間結果。
int a[MAX],b[MAX];
//初始化a
memset(a,0,sizeof(a));
a[0]=1;
for (int i=1;i<=17;i++)//迴圈每個因子
{
memset(b,0,sizeof(b));
for (int j=n1[i];j<=n2[i]&&j*v[i]<=P;j++)//迴圈每個因子的每一項
for (int k=0;k+j*v[i]<=P;k++)//迴圈a的每個項
b[k+j*v[i]]+=a[k];//把結果加到對應位
memcpy(a,b,sizeof(b));//b賦值給a
}
P是可能的最大指數。拿鈔票組合這題來說,如果要求15元有多少組合,那麼P就是15;如果問最小的不能拼出的數值,那麼P就是所有錢加起來的和。P有時可以直接省略。具體請看本文後面給出的例題。
如果n2是無窮,那麼第二層迴圈條件j<=n2[i]可以去掉。
如何提高效率?
用一個last變數記錄目前最大的指數,這樣只需要在0..last上進行計算。
這裡給出第二個模板:
//初始化a,因為有last,所以這裡無需初始化其他位
a[0]=1;
int last=0;
for (int i=0;i<K;i++)
{
int last2=min(last+n[i]*v[i],P);//計算下一次的last
memset(b,0,sizeof(int)*(last2+1));//只清空b[0..last2]
for (int j=n1[i];j<=n2[i]&&j*v[i]<=last2;j++)//這裡是last2
for (int k=0;k<=last&&k+j*v[i]<=last2;k++)//這裡一個是last,一個是last2
b[k+j*v[i]]+=a[k];
memcpy(a,b,sizeof(int)*(last2+1));//b賦值給a,只賦值0..last2
last=last2;//更新last
}
例題
下面看幾個例題。
一、hdu 1085和hdu 1171兩題套用了第二個模板,省略了程式碼中二三層迴圈裡關於last2的條件(其實也可以加上)。
詳見:
hdu 1085:http://blog.csdn.net/xiaofei_it/article/details/17041467
hdu 1171:http://blog.csdn.net/xiaofei_it/article/details/17041709
二、hdu 1398套用了第一個模板,因為n2中每一項為無窮大,所以n2陣列就省略了。
詳見:
hdu 1398:http://blog.csdn.net/xiaofei_it/article/details/17041815
三、hdu 2079、hdu 2082和hdu 2110三題直接套用了第二個模板。
詳見:
hdu 2079:http://blog.csdn.net/xiaofei_it/article/details/17042045
hdu 2082:http://blog.csdn.net/xiaofei_it/article/details/17042253
hdu 2110:http://blog.csdn.net/xiaofei_it/article/details/17042421
另外,至於什麼時候用第一個模板,什麼時候用第二個模板,就看題目規模。
通常情況下,第一個模板就夠用了,上面的那些用第二個模板的題目用第一個模板同樣能AC。
但如果資料規模比較大(通常不會有這種情況),就要使用第二個模板了。
以上題目n1均為0。
四、hdu 2152是一道n1不為0的題目,我在這裡分別套用第一個和第二個模板解題。
詳見:
hdu 2152:http://blog.csdn.net/xiaofei_it/article/details/17042497
希望大家讀了我的這篇文章後能有收穫。
相關文章
- HDU 1709 The Balance(母函式)函式
- 函式詳解 | VLOOKUP 函式:最為人熟知的偵探函式
- HDOJ-1398 Square Coins(母函式)函式
- 杭電ACM hdu 2152 Fruit 解題報告(母函式)ACMUI函式
- C++的函式和模板函式 (轉)C++函式
- Socket send函式和recv函式詳解函式
- 杭電ACM hdu 1398 Square Coins 解題報告(母函式)ACM函式
- HDU 1028 Ignatius and the Princess III:dp or 母函式函式
- HDU 2082-找單詞(母函式-有限次)函式
- 杭電ACM hdu 2110 Crisis of HDU 解題報告(母函式)ACM函式
- 史上最通俗分散式鎖解讀分散式
- 通用函式和條件表示式函式
- ORALCE函式:LAG()和LEAD() 分析函式詳解函式
- webgl內建函式--通用函式Web函式
- C++ 函式過載,函式模板和函式模板過載,選擇哪一個?C++函式
- 杭電ACM hdu 1171 Big Event in HDU 解題報告(母函式)ACM函式
- 杭電ACM hdu 2082 找單詞 解題報告(母函式)ACM函式
- 函式模板函式
- PL/SQL單行函式和組函式詳解(轉)SQL函式
- SQL中常用的字串LEFT函式和RIGHT函式詳解!SQL字串函式
- Numpy 通用函式函式
- 杭電ACM hdu 2079 選課時間 解題報告(母函式)ACM函式
- C++中建構函式,拷貝建構函式和賦值函式的詳解C++函式賦值
- vue3函式setUp和reactive函式詳細講解Vue函式React
- 模板函式的特化函式
- 詳解Java函式式介面Java函式
- 函式過載與函式模板的區別函式
- 尤拉函式詳解函式
- malloc函式詳解函式
- kill() 函式詳解函式
- ioctl()函式詳解函式
- gluLookAt 函式詳解函式
- fopencookie函式詳解Cookie函式
- Smarty 模板函式函式
- setjmp 和 longjmp 函式使用詳解函式
- ORACLE單行函式與多行函式之六:通用函式示例Oracle函式
- PHP函式處理函式例項詳解PHP函式
- jQuery使用最廣泛的javascript函式庫jQueryJavaScript函式