1. 程式人生 > >普通型母函式

普通型母函式

生成函式(母函式)普通生成函式指數生成函式

1.普通生成函式用於解決多重集的組合問題

2.指數型母函式用於解決多重集的排列問題

母函式可以解決遞迴數列的通項問題:斐波那契數列、卡特蘭數列等

普通母函式:

    構造母函式G(x), G(x) = a0 + a1*x + a2*clip_image002 + a3*clip_image002[4] +....+ an*clip_image002[6],  則稱G(x)是數列a0,a1…an的母函式。

    通常普通母函式用來解多重集的組合問題,其思想就是構造一個函式來解決問題,一般過程如下:

    1.建立模型:

物品n種,每種數量分別為k1,k2,..kn個,每種物品又有一個屬性值p1,p2,…pn,(如本題的字母價值),求屬性值

為m的物品組合方法數。(若數量ki無窮 也成立,即對應下面式子中第ki項的指數一直到無窮)

    2.構造母函式:

G(x)=(1+clip_image002[18]+clip_image002[20]clip_image002[22])(1+clip_image002[26]+clip_image002[28]+…clip_image002[30])…(1+clip_image002[32]+clip_image002[34]+…clip_image002[38])         (一)

          =a0 + a1*x + a2*clip_image002 + a3*clip_image002[4] +....+ akk*clip_image002[16]     (設kk=k1·p1+k2·p2+…kn·pn)         (二)

                  G(x)含義: ak 為屬性值為k的組合方法數。

母函式利用的思想:

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

    2.把離散數列和冪級數對應起來,把離散數列間的相互結合關係對應成為冪級數間的運算關係,最後由冪級數形式來

       確定離散數列的構造。

程式碼實現:

    求G(x)時一項一項累乘。先令G=1=(1+0*x+0*clip_image002[56]+…0*clip_image002[58]),再令G=G*(1+clip_image002[18]+clip_image002[20]clip_image002[22])得到形式(二)的式子…最後令G=G*(1+clip_image002[32]+clip_image002[34]+…clip_image002[38])。

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

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

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

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

……

解題過程:

首先要寫出表示式,通常是多項式的乘積,每項由多個 x^{y} 組成。

通用表示式為:

(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