普通型母函式
生成函式(母函式)有普通生成函式和指數生成函式:
1.普通生成函式用於解決多重集的組合問題
2.指數型母函式用於解決多重集的排列問題
母函式可以解決遞迴數列的通項問題:斐波那契數列、卡特蘭數列等
普通母函式:
構造母函式G(x), G(x) = a0 + a1*x + a2* + a3* +....+ an*, 則稱G(x)是數列a0,a1…an的母函式。
通常普通母函式用來解多重集的組合問題,其思想就是構造一個函式來解決問題,一般過程如下:
1.建立模型:
物品n種,每種數量分別為k1,k2,..kn個,每種物品又有一個屬性值p1,p2,…pn,(如本題的字母價值),求屬性值和
2.構造母函式:
G(x)=(1++…)(1+++…)…(1+++…) (一)
=a0 + a1*x + a2* + a3* +....+ akk* (設kk=k1·p1+k2·p2+…kn·pn) (二)
G(x)含義: ak 為屬性值和為k的組合方法數。
母函式利用的思想:
1.把組合問題的加法法則和冪級數的乘冪對應起來。
2.把離散數列和冪級數對應起來,把離散數列間的相互結合關係對應成為冪級數間的運算關係,最後由冪級數形式來
確定離散數列的構造。
程式碼實現:
求G(x)時一項一項累乘。先令G=1=(1+0*x+0*+…0*),再令G=G*(1++…)得到形式(二)的式子…最後令G=G*(1+++…)。
普通母函式通常解決類似如下的問題:
給5張1元,4張2元,3張5元,要得到15元,有多少種組合?
某些時候會規定至少使用3張1元、1張2元、0張5元。
某些時候會規定有無數張1元、2元、5元。
……
解題過程:
首先要寫出表示式,通常是多項式的乘積,每項由多個 組成。
通用表示式為:
(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個因子的終止係數,對應具體問題中的每個物品的最多個數,即最多要取多少個
解題的關鍵是要確定V,N1,N2陣列的值
模板
//a為計算結果,b為中間結果
int a[MAX],b[MAX];
memset(a,0,sizeof a);
a[0]=1;
for(int i=1;i<=k;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];//把結果加到對應位
memccpy(a,b,sizeof b);//b賦值給a
}
p是可能的最大指數,拿鈔票組合的題來說,如果求15元有多少種組合,那麼p就是15;如果問最小的不能拼出的數值,那麼p就是所有錢加起來的和。
用一個last變數記錄目前最大的指數,這樣只需要在0--last上計算
改進模板
//初始化a,因為有last,所以這裡無需初始化其他位
int last=0;
a[0]=1;
for(int i=1;i<=n;i++) {//迴圈每個因子
int next=min(last+n2[i]*v[i],p);//計算下一次的last
for(int j=n1[i];j<=n2[i]&&j*v[i]<=next;j++)//迴圈每個因子的每一項
for(int k=0;k<=last&&k+j*v[i]<=next;k++)//迴圈a的每一項
b[k+j*v[i]]+=a[k];//把結果加到對應位
for(int k=0;k<maxn;k++) {//賦值
a[k]=b[k]%mod;
b[k]=0;
}
last=next;//更新last
}
例題
一、hdu 1085和hdu 1171兩題套用了第二個模板,省略了程式碼中二三層迴圈裡關於last2的條件(其實也可以加上)。
詳見:
hdu1085 :http://blog.csdn.net/xiaofei_it/article/details/17041467
import java.util.*;
import java.math.*;
public class Main {
public static int maxn=8010;
public static void main(String[] args) {
int[] a=new int[maxn];//儲存結果,a[i]表示組成i種水果的方案數為a[i]
int[] b=new int[maxn];//中間結果
int[] n1=new int[maxn];//第i種水果最少的個數
int[] n2=new int[maxn];//第i種水果最多的個數
int[] v=new int[maxn];//第i種水果的價值
Scanner cin=new Scanner(System.in);
//int T=cin.nextInt();
while(cin.hasNext()) {
for(int i=0;i<maxn;i++) {
a[i]=0;
b[i]=0;
n1[i]=0;
n2[i]=0;
}
v[1]=1;v[2]=2;v[3]=5;
int sum=0;
for(int i=1;i<=3;i++) {
n2[i]=cin.nextInt();
sum+=n2[i];
}
if(sum==0) break;
a[0]=1;
int p=0;
//要求最小不能拼出的錢,p就是所有錢加起來的和
for(int i=1;i<=3;i++)
p=p+v[i]*n2[i];
for(int i=1;i<=3;i++) {
for(int k=0;k<maxn;k++)
b[k]=0;
for(int j=n1[i];j<=n2[i]&&j*v[i]<=p;j++)
for(int k=0;k+j*v[i]<=p;k++)
b[k+j*v[i]]+=a[k];
for(int k=0;k<maxn;k++)
a[k]=b[k];
}
int ans=0;
//遍歷找出方案數為0的錢幣組合
for(int i=1;i<maxn;i++)
if(a[i]==0) {
ans=i;
break;
}
System.out.println(ans);
}
cin.close();
}
}
hdu 1171:http://blog.csdn.net/xiaofei_it/article/details/17041709
import java.util.*;
import java.math.*;
public class Main {
public static int maxn=250000,maxm=60;
public static void main(String[] args) {
int[] a=new int[maxn];//儲存結果,a[i]表示組成i種水果的方案數為a[i]
int[] b=new int[maxn];//中間結果
int[] n1=new int[maxm];//第i種水果最少的個數
int[] n2=new int[maxm];//第i種水果最多的個數
int[] v=new int[maxm];//第i種水果的價值
Scanner cin=new Scanner(System.in);
//int T=cin.nextInt();
while(cin.hasNext()) {
for(int i=0;i<maxn;i++) {
a[i]=0;
b[i]=0;
}
for(int i=0;i<maxm;i++) {
n1[i]=0;
n2[i]=0;
v[i]=0;
}
int n=cin.nextInt();
if(n<0) break;
int sum=0;
for(int i=1;i<=n;i++) {
v[i]=cin.nextInt();
n2[i]=cin.nextInt();
sum+=v[i]*n2[i];
}
int p=sum;
//if(sum%2==1) p++;
a[0]=1;
for(int i=1;i<=n;i++) {
for(int k=0;k<maxn;k++)
b[k]=0;
for(int j=n1[i];j<=n2[i]&&j*v[i]<=p;j++)
for(int k=0;k+j*v[i]<=p;k++)
b[k+j*v[i]]+=a[k];
for(int k=0;k<maxn;k++)
a[k]=b[k];
}
int ans=0;
for(int i=p/2;i>=0;i--) {
if(a[i]!=0) {
ans=i;
break;
}
}
System.out.println(p-ans+" "+ans);
}
cin.close();
}
}
二、hdu 1398套用了第一個模板,因為n2中每一項為無窮大,所以n2陣列就省略了。
詳見:
hdu 1398:http://blog.csdn.net/xiaofei_it/article/details/17041815
import java.math.*;
import java.util.*;
public class Main {
public static int maxn=300*300+10;
static int[] a=new int[maxn];
static int[] b=new int[maxn];
static int[] v=new int[30];
static void init() {
for(int i=0;i<maxn;i++) {
a[i]=0;b[i]=0;
}
for(int i=1;i<=17;i++)
v[i]=i*i;
a[0]=1;
for(int i=1;i<=17;i++) {
for(int j=0;j*v[i]<=300;j++)
for(int k=0;k+j*v[i]<=300;k++)
b[k+j*v[i]]+=a[k];
for(int k=0;k<maxn;k++) {
a[k]=b[k];
b[k]=0;
}
}
}
public static void main(String[] args) {
Scanner cin=new Scanner(System.in);
init();
while(cin.hasNext()) {
int n=cin.nextInt();
if(n==0) break;
System.out.println(a[n]);
}
cin.close();
}
}
三、hdu 2079、hdu 2082和hdu 2110三題直接套用了第二個模板。
詳見:
hdu2079
http://blog.csdn.net/xiaofei_it/article/details/17042045
import java.math.*;
import java.util.*;
public class Main {
public static int maxn=400;
static int[] a=new int[maxn];
static int[] b=new int[maxn];
static int[] v=new int[maxn];
static int[] n1=new int[maxn];
static int[] n2=new int[maxn];
static void init() {
for(int i=0;i<maxn;i++) {
a[i]=0;b[i]=0;
v[i]=0;
n1[i]=0;n2[i]=0;
}
}
public static void main(String[] args) {
Scanner cin=new Scanner(System.in);
int T=cin.nextInt();
while(T!=0) {
T--;
init();
int p=cin.nextInt();
int n=cin.nextInt();
for(int i=1;i<=n;i++) {
v[i]=cin.nextInt();
n2[i]=cin.nextInt();
}
a[0]=1;
for(int i=1;i<=n;i++) {
for(int j=n1[i];j<=n2[i]&&j*v[i]<=p;j++)
for(int k=0;k+j*v[i]<=p;k++)
b[k+j*v[i]]+=a[k];
for(int k=0;k<maxn;k++) {
a[k]=b[k];
b[k]=0;
}
}
System.out.println(a[p]);
}
cin.close();
}
}
hdu2082:
http://blog.csdn.net/xiaofei_it/article/details/17042253
import java.util.*;
import java.math.*;
public class Main {
public static int maxn=110;
public static void main(String[] args) {
int[] a=new int[maxn];//儲存結果,a[i]表示組成i種水果的方案數為a[i]
int[] b=new int[maxn];//中間結果
int[] n1=new int[maxn];//第i種水果最少的個數
int[] n2=new int[maxn];//第i種水果最多的個數
int[] v=new int[maxn];//第i種水果的價值
Scanner cin=new Scanner(System.in);
int T=cin.nextInt();
while(T!=0) {
T--;
for(int i=0;i<maxn;i++) {
a[i]=0;
b[i]=0;
n1[i]=0;
n2[i]=0;
v[i]=i;
}
for(int i=1;i<=26;i++)
n2[i]=cin.nextInt();
a[0]=1;
int p=50;
for(int i=1;i<=26;i++) {
for(int k=0;k<maxn;k++)
b[k]=0;
for(int j=n1[i];j<=n2[i]&&j*v[i]<=p;j++)
for(int k=0;k+j*v[i]<=p;k++)
b[k+v[i]*j]+=a[k];
for(int k=0;k<maxn;k++)
a[k]=b[k];
}
long ans=0;
for(int i=1;i<=p;i++)
ans+=a[i];
System.out.println(ans);
}
cin.close();
}
}
hdu 2110:
http://blog.csdn.net/xiaofei_it/article/details/17042421
import java.math.*;
import java.util.*;
public class Main {
static int mod=10000;
public static int maxn=10000+7;
static long[] a=new long[maxn];
static long[] b=new long[maxn];
static int[] v=new int[maxn];
static int[] n1=new int[maxn];
static int[] n2=new int[maxn];
static int min(int a,int b) {
return a<b?a:b;
}
static void init() {
for(int i=0;i<maxn;i++) {
a[i]=0;b[i]=0;
v[i]=0;
n1[i]=0;n2[i]=0;
}
}
public static void main(String[] args) {
Scanner cin=new Scanner(System.in);
//int T=cin.nextInt();
while(cin.hasNext()) {
init();
int n=cin.nextInt();
if(n==0) break;
int p=0;
for(int i=1;i<=n;i++) {
v[i]=cin.nextInt();
n2[i]=cin.nextInt();
p+=v[i]*n2[i];
}
if(p%3!=0) {
System.out.println("sorry");
continue;
}
p/=3;
int last=0;
a[0]=1;
for(int i=1;i<=n;i++) {
int next=min(last+n2[i]*v[i],p);//計算下一次的last
for(int j=n1[i];j<=n2[i]&&j*v[i]<=next;j++)
for(int k=0;k<=last&&k+j*v[i]<=next;k++)
b[k+j*v[i]]+=a[k];
for(int k=0;k<maxn;k++) {
a[k]=b[k]%mod;
b[k]=0;
}
last=next;//更新last
}
if(a[p]!=0)
System.out.println(a[p]%mod);
else
System.out.println("sorry");
}
cin.close();
}
}
另外,至於什麼時候用第一個模板,什麼時候用第二個模板,就看題目規模。 通常情況下,第一個模板就夠用了,上面的那些用第二個模板的題目用第一個模板同樣能AC。 但如果資料規模比較大(通常不會有這種情況),就要使用第二個模板了。 以上題目n1均為0。
四、hdu 2152是一道n1不為0的題目,我在這裡分別套用第一個和第二個模板解題。
詳見: hdu 2152:http://blog.csdn.net/xiaofei_it/article/details/17042497
C++
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=130;
int a[maxn],b[maxn];
int n1[maxn],n2[maxn];
int v[maxn];
void init()
{
memset(a,0,sizeof a);
memset(b,0,sizeof b);
memset(n1,0,sizeof n1);
memset(n2,0,sizeof n2);
}
int main()
{
for(int i=0;i<maxn;i++)
v[i]=1;
int n,m;
while(scanf("%d%d",&n,&m)!=EOF){
init();
for(int i=1;i<=n;i++)
scanf("%d%d",&n1[i],&n2[i]);
a[0]=1;
for(int i=1;i<=n;i++)
{
memset(b,0,sizeof b);
for(int j=n1[i];j<=n2[i]&&j*v[i]<=m;j++)
for(int k=0;k+j*v[i]<=m;k++)
b[k+v[i]*j]+=a[k];
//memcpy(a,b,sizeof b);
for(int k=0;k<maxn;k++)
a[k]=b[k];
}
printf("%d\n",a[m]);
}
}
Java
import java.util.*;
import java.math.*;
public class Main {
public static int maxn=110;
public static void main(String[] args) {
int[] a=new int[maxn];//儲存結果,a[i]表示組成i種水果的方案數為a[i]
int[] b=new int[maxn];//中間結果
int[] n1=new int[maxn];//第i種水果最少的個數
int[] n2=new int[maxn];//第i種水果最多的個數
int[] v=new int[maxn];//第i種水果的價值
Scanner cin=new Scanner(System.in);
//int T=cin.nextInt();
while(cin.hasNext()) {
for(int i=0;i<maxn;i++) {
a[i]=0;b[i]=0;n1[i]=0;n2[i]=0;
v[i]=1;
}
int n=cin.nextInt();
int m=cin.nextInt();
for(int i=1;i<=n;i++) {
n1[i]=cin.nextInt();
n2[i]=cin.nextInt();
}
a[0]=1;
for(int i=1;i<=n;i++) {//迴圈每一個因子
for(int k=0;k<maxn;k++)
b[k]=0;
for(int j=n1[i];j<=n2[i]&&j*v[i]<=m;j++)//迴圈每個因子的每一項
for(int k=0;k+j*v[i]<=m;k++)//迴圈a的每一項
b[k+j*v[i]]+=a[k];//把結果加到對應的位
for(int k=0;k<maxn;k++)//b賦值給a
a[k]=b[k];
}
System.out.println(a[m]);
}
cin.close();
}
}
相關推薦
hdu 1028(普通型母函式)
Ignatius and the Princess III Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others) Total Submission(s): 6
hdu 1709(普通型母函式)
The Balance Time Limit: 1000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 3249 Accepted Su
普通型母函式原理及模板程式碼詳解
母函式有很多種,最常用的有普通型母函式和指數型母函式。兩者區別是:普通型母函式主要是來求組合的方案數,而指數型母函式是求多重排列數。下面只講解普通型母函式的相關知識。定義:若函式G(x)=a0+a1*x
普通型母函式
生成函式(母函式)有普通生成函式和指數生成函式: 1.普通生成函式用於解決多重集的組合問題 2.指數型母函式用於解決多重集的排列問題 母函式可以解決遞迴數列的通項問題:斐波那契數列、卡特蘭數列等 普通母函式: 構造母函式G(x), G(x) = a0 + a
普通型母函式和指數型母函式
母函式:摘自百度百科 生成函式即母函式,是組合數學中尤其是計數方面的一個重要理論和工具。生成函式有普通型生成函式和指數型生成函式兩種,其中普通型用的比較多。形式上說,普通型生成函式用於解決多重集的組合問題,而指數型母函式用於解決多重集的排列問題。母函式還可以解決遞迴數列的通
B.找單詞——(HDU 2082 普通型母函式)
傳送門 找單詞 Time Limit: 1000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Total S
hdu 1171(普通型母函式)
Big Event in HDU Time Limit: 10000/5000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others) Total Submission(s): 11501 Acce
hdu 2082--找單詞 普通型母函式的應用
B - 找單詞 Time Limit:1000MS Memory Limit:32768KB 64bit IO Format:%I64d & %I64u De
組合數學-普通型生成函式(母函式)-hdu2082 找單詞
Problem Description 假設有x1個字母A, x2個字母B,..... x26個字母Z,同時假設字母A的價值為1,字母B的價值為2,..... 字母Z的價值為26。那麼,對於給定的字母,可以找到多少價值<=50的單詞呢?單詞的價值就是組成一個單詞的所有字母的價值之和,比
HDU 2082 普通型母函數
ace () -1 分析 show pac include 分享 amp 分析: 組成單詞好說,價值如何體現? 改變指數就行,例如: 這樣,組成的單詞,指數就是權值,多項式相乘,指數小於50的就OK; 1 #include <b
母函式第二彈 之 真正的母函式入門
閒話: 上次第一期更完了之後,博主覺得題目貌似還是相對來說trl。 於是博主覺得在這一期除了介紹母函式以外,還帶來了一波富有思維量的 毒瘤題 水題。 嗯,就這樣吧。閒話不多說,現在就開講了! 定義: 生成函式即母函式,是組合數學中尤其是計數方面的一個重要理論和工具。 生成函式有普通
sincerit 母函式(組合問題)
大佬程式碼: https://blog.csdn.net/yu121380/article/details/79914529 https://blog.csdn.net/xiaofei_it/article/details/17042651?utm_source=blogxgwz0 有1克、
母函式模板核心
轉載至 http://blog.csdn.net/xiaofei_it/article/details/17042651 母函式,又稱生成函式,是ACM競賽中經常使用的一種解題演算法,常用來解決組合方面的題目。 本文講解母函式,但不講解該演算法的基礎理論。
【POJ - 1664】放蘋果 (遞迴經典題 或 dp 或 母函式)
題幹: 把M個同樣的蘋果放在N個同樣的盤子裡,允許有的盤子空著不放,問共有多少種不同的分法?(用K表示)5,1,1和1,5,1 是同一種分法。 Input 第一行是測試資料的數目t(0 <= t <= 20)。以下每行均包含二個整數M和N,以空格分開。1<=M,N&
HDOJ-1398 Square Coins(母函式/DP)
題目描述 Square Coins Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others) Total Submission(s): 1
母函式初步
定義 對於序列a0,a1,a2,…,an構造一函式 G(x) = a0+a1*x+a2*x2+a3*x3+…+an*xn 則稱G(x)是序列a0,a1,a2,…,an的母函式。 在下面的例題中可以看到母函式可以將問題的複雜度從窮舉(NN)降為N3。 應用 例1:若有1克、2克
HDU_2082 找單詞 【母函式的應用】
題目: 假設有x1個字母A, x2個字母B,..... x26個字母Z,同時假設字母A的價值為1,字母B的價值為2,..... 字母Z的價值為26。那麼,對於給定的字母,可以找到多少價值<=50的單詞呢?單詞的價值就是組成一個單詞的所有字母的價值之和,比如,單詞ACM的價值是1+3+14=
hdu2065"紅色病毒"問題(指數母函式+快速冪取模)
"紅色病毒"問題 Time Limit: 1000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 9329  
CF 284E 拓撲排序+母函式
題目連結:https://vjudge.net/contest/257979#problem/L 題目思路:首先對於(bi,ci)的限制,建圖,用拓撲排序,先反向建圖,然後t-=d*a[i],d表示深度,反向建圖in為0的d為1,接著對於樣例1,想新增一個2,,3和4也要跟著加進去,然後用拓撲排
母函式入門練習
HDU2082 假設有x1個字母A, x2個字母B,..... x26個字母Z,同時假設字母A的價值為1,字母B的價值為2,..... 字母Z的價值為26。那麼,對於給定的字母,可以找到多少價值<=50的單詞呢?單詞的價值就是組成一個單詞的所有字母的價值之和,比如,單詞ACM的價值是1+3