python 子函数返回母函数结果_算法课-母函数专题

【生成函数解析】

由于CSDN更新过好几个版本,导致一些图片的丢失,但一点都不影响解析生成函数。

利用D题的选课时间作为模版。

给定m个物品的价格和数量,构成价值为n的方案数

1 #include

2 #include

3

4 using namespacestd;5 const int N = 50;6 intn , m ;7 inta[N] , b[N] , c[N] ;8 intf[N] , tmp[N] ;9 intmain()10 {11 intT ;12 scanf("%d",&T);13 while( T--){14 //初始化

15 for( int i = 0 ; i < N ; i++ ) tmp[i] = f[i] = 0;16 f[0] = 1;17 scanf("%d%d",&n,&m);18

19 int Highest_Pow = 0, Limit;20 //枚举物品

21 for( int i = 1 ; i <= m ; i++){22 scanf("%d%d",&a[i],&b[i]);23

24 Limit =min( Highest_Pow , n );25

26 //枚举第一个括号中的次幂

27 for( int j = 0 ; j <= Limit ; j++){28 //枚举第二个括号中的个数,通过它的价值a[i],枚举每一项指数 应为k*a[i]

29 for( int k = 0 ; j + k * a[i] <= n && k <= b[i] ; k++){30 tmp[ j + k*a[i] ] +=f[j];31 }32 }33

34 //更新最高次幂

35 Highest_Pow += a[i] *b[i] ;36 Limit =min( Highest_Pow , n );37 for( int j = 0 ; j <= Limit ; j++){38 f[j] =tmp[j] ;39 tmp[j] = 0;40 }41 }42 printf("%d\n",f[n]);43 }44 return 0;45 }

模版1

1 #include

2 #include

3

4 using namespacestd;5 const int N = 50;6 intn , m ;7 inta[N] , b[N] , c[N] ;8 intf[N] , tmp[N] ;9 intmain()10 {11 intT ;12 scanf("%d",&T);13 while( T--){14 //初始化

15 for( int i = 0 ; i < N ; i++ ) tmp[i] = f[i] = 0;16 f[0] = 1;17 scanf("%d%d",&n,&m);18

19 int Highest_Pow = 0, Limit;20 //枚举物品

21 for( int i = 1 ; i <= m ; i++){22 scanf("%d%d",&a[i],&b[i]);23

24 Limit =min( Highest_Pow , n );25

26 //枚举第一个括号中的次幂

27 for( int j = 0 ; j <= Limit ; j++){28 //枚举第二个括号中的个数,通过它的价值a[i],枚举每一项指数 应为k*a[i]

29 for( int k = 1 ; j + k * a[i] <= n && k <= b[i] ; k++){30 tmp[ j + k*a[i] ] +=f[j];31 }32 }33

34 //更新最高次幂

35 Highest_Pow += a[i] *b[i] ;36 Limit =min( Highest_Pow , n );37 for( int j = 0 ; j <= Limit ; j++){38 f[j] +=tmp[j] ;39 tmp[j] = 0;40 }41 }42 printf("%d\n",f[n]);43 }44 return 0;45 }

模版2

A - Holding Bin-Laden Captive!

【题目来源】HDU - 1085

【题意】

给定n1,n2,n3分别代表硬币面值为1,2,5元的数量

请输出由这些硬币 凑不成的最小正整数?

例如:

有1个面值为1元的硬币。

有1个面值为2元的硬币。

有3个面值为5元的硬币。

即可构成:{1,2,3,5,6,7,8,10,11,12,13,15,16,17,18}

集合中凑不成的最小正整数为4.

【题解】

基本上就是母函数(生成函数)的模板题

根据题目的样例构造出

( x^0 + x^1 ) * ( x^0 + x^2 + x^4 ) * ( x^0 + x^5 + x^10 + x^15)

请大家用草稿纸手动算一下上述式子。

你就会发现能凑出来的数字会是拥有指数为{1,2,3,5,6,7,8,10,11,12,13,15,16,17,18}的多项式。而我们想要找的答案只需要遍历哪个指数未出现过即可。

这种题型被称为:普通型的母函数(生成函数)

其特点为:给定 ”面值” 和 “数量” 问其“构成的方案数”或“能否构成”。

而这个题目是:固定面值,给出数量,问起能否构成。

算法复杂度O(n^3)

计算过程就是模拟多项式乘法,()*()两个多项式之间进行乘法运算。

1 #include

2 const int N = 8e3 + 10;3 int weight[3] = { 1 , 2 , 5};4 int num[3] ;5 intf[N] ;6 inttmp[N] ;7 intmain()8 {9 while( scanf("%d%d%d",&num[0],&num[1],&num[2]) , (num[0]+num[1]+num[2]) ){10 //初始化

11 f[0] = 1;12 for( int i = 1 ; i < N ; i++ ) f[i] = 0;13

14 //枚举第一个括号

15 for( int i = 1 ; i <= num[0] ; i++ ) f[i] = 1;16

17 int Highest_Pow = 0;18

19 //第一层枚举 硬币种类

20 for( int i = 0 ; i < 3 ; i ++){21 //第二层枚举 第一个括号中 从0次幂到最高次幂

22 for( int j = 0 ; j <= Highest_Pow ; j ++){23 //第三层枚举 第二个括号中的数量

24 for( int k = 1 ; k <= num[i] ; k++){25 f[ j + k * weight[i] ] |=f[j];26 }27 }28 //更新最高次幂

29 Highest_Pow += weight[i] *num[i] ;30 }31

32 for( int i = 1 ; i < N ; i++){33 if( !f[i] ){34 printf("%d\n",i);35 break;36 }37 }38 }39 return 0;40 }

Holding Bin-Laden Captive!-母函数

扩展做法-多重背包

我们习惯做的是01背包问题,多重背包仅仅是多增加了一个维度:"物品数量"任何"普通型母函数问题" 都可以转化成 "多重背包问题"多重背包模型会比母函数更加好写。

问题转化是一样的,都是根据生成函数的特点而设计。

仅仅是计算的角度不一样。

同时,多重背包灵活性体现在复杂度

朴素做法 O(N^3) -> 二进制优化 O(N * V *logN ) -> 单调队列优化 O(N *V)

1 //扩展(多重背包做法-朴素做法)

2 #include

3 const int N = 8e3 + 20;4

5 intf[N];6 int num[3];7 int weight[3] = { 1 , 2 , 5};8

9 intmain()10 {11 while( scanf("%d%d%d",&num[0],&num[1],&num[2]) , ( num[0] + num[1] + num[2] ) ){12 f[0] = 1;13 for( int i = 1 ; i < N ; i++ ) f[i] = 0;14

15 int V = num[0] + num[1] * 2 + num[2] * 5;16 //枚举物品个数

17 for( int i = 0 ; i < 3 ; i ++){18 //枚举每个物品的重量

19 for( int j = V ; j >= weight[i]; j --){20 //在j容量限制下枚举物品个数,同时进行01背包处理

21 for( int k = 1 ; k <= num[i] ; k++){22 if( j - k * weight[i] >= 0){23 f[j] |= f[j - k *weight[i]] ;24 }25 }26 }27 }28

29 for( int i = 1 ; i < N ; i++){30 if( !f[i] ){31 printf("%d\n",i);32 break;33 }34 }35 }36 return 0;37 }

扩展(多重背包做法-朴素做法)

B - Big Event in HDU

【题目来源】HDU - 1171

【题意】

给定n个物品的“价值”,“数量”

请问如果尽可能划分出两堆物品 ,要求 两堆物品间价值之差 尽可能小。

例如:

有1个物品价值为10

有2个物品价值为20

有1个物品价值为30

通过划分两堆物品 {10,30} = 40 ,{20,20} = 40所以答案为40 40划分两堆物品如果价值不同,请先输出价值大的一堆,后输出价值小的一堆

【生成函数做法】

和上一个题目类似

给定物品的价值和数量,问是否存在尽可能靠近total/2的值。

首先处理好输入,初始化工作。

然后进行生成函数模板代入。

最后遍历,找出尽可能靠近total/2的情况

1 #include

2 const int N = 5e6 + 10;3 const int M = 1e3 + 10;4 intf[N] ;5 intnum[M] , w[M] ;6 intmain()7 {8 intn , Highest_Pow , total ;9 while( scanf("%d",&n) ,( n>0) ){10 total = 0;11 for( int i = 0 ; i < n ; i ++){12 scanf("%d%d",&w[i] , &num[i] );13 total += w[i] *num[i] ;14 }15 for (int i = 1 ; i <= total / 2 ; i++ ) f[i] = 0;16 f[0] = 1;17 Highest_Pow = 0;18

19 for( int i = 0 ; i < n ; i ++){20 for( int j = 0 ; j <= Highest_Pow ; j ++){21 for( int k = 1 ; k <= num[i] ; k++){22 f[j+k*w[i]] |=f[j] ;23 }24 }25 Highest_Pow += w[i] *num[i] ;26 }27 for( int i = total/2 ; i >= 0 ; i--){28 if( f[i] ){29 printf("%d %d\n",total-i,i);30 break;31 }32 }33 }34 return 0;35 }

Big Event in HDU-母函数做法

【01背包做法】

经典的背包问题,01背包

首先限制背包容量为总容量的一半。

然后把所有的物品离散化,同时进行01背包处理后。

答案为: {total- f[V] , f[V]}

1 #include

2 #include

3 using namespacestd;4

5 const int N = 5e6 + 10;6 const int M = 1e3 + 10;7 intf[N];8 intw[M];9 intnum[M];10 intmain()11 {12 intn , V , total ;13 while( scanf("%d",&n) , ( n > 0) ){14

15 total = V = 0;16

17 for( int i = 0 ; i < n ; i++){18 scanf("%d%d",&w[i] , &num[i] );19 total += w[i] *num[i] ;20 }21 V = total / 2;22

23 for( int i = 0 ; i <= V ; i ++ ) f[i] = 0;24

25 for( int i = 0 ; i < n ; i++){26 while( num[i]--){27 for( int j = V ; j >= w[i] ; j--){28 f[j] = max( f[j] , f[j-w[i]] +w[i] );29 }30 }31 }32

33 printf("%d %d\n",total -f[V] , f[V] );34 }35 return 0;36 }

Big Event in HDU-01背包做法

C - Square Coins

【题目来源】HDU - 1398

【题意】

给定价值为1,4,9……289(17^2)的硬币

给定一个值,请问有多少种构造的方法?

例如:10 = 1 + 1 …… + 1

10 = 1 + 1 + 1 + 1 + 1 + 1 + 4

10 = 1 + 1 + 4 + 4

10 = 1 + 9答案就是4

【生成函数做法】

这个题目和课堂上讲的例题一模一样,仅仅是面值发生改变,其余的都没有变。

该题的普通母函数的结构为"面值固定","数量不定",求解构成某个数值的方案数?

1 #include

2 using namespacestd;3 const int M = 305;4 const int N = 20;5 intf[M];6 intv[N];7 inttmp[M];8 voidInit(){9 for( int i = 0 ; i < 17 ; i++){10 v[i] = ( i+1 ) * ( i+1);11 }12 f[0] = 1;13 for( int i = 0 ; i < 17 ; i ++){14 for( int j = 0 ; j <= 300 - v[i] ; j++){15 for( int k = 1 ; j + k*v[i] < M ; k++){16 tmp[ j+k*v[i] ] +=f[j] ;17 }18 }19 for(int j = 1 ; j <= 300 ; j++){20 f[j] +=tmp[j] ;21 tmp[j] = 0;22 }23 }24 }25 intmain(){26

27 intn ;28 Init();29 while( scanf("%d",&n) , n > 0){30 printf("%d\n",f[n]);31 }32 return 0;33 }

Square Coins-母函数

【完全背包做法】

完全背包 指的是 “物品数量无限”的背包问题

这个题目其实就是和之前生成函数有所区别在于个数不再受限。

所以问题可以转变成可取无限多次的背包问题->"完全背包"

1 #include

2 using namespacestd;3 const int M = 305;4 const int N = 20;5 intf[M];6 intv[N];7 inttmp[M];8 voidInit(){9 for( int i = 0 ; i < 17 ; i++){10 v[i] = ( i+1 ) * ( i+1);11 }12 f[0] = 1;13 for( int i = 0 ; i < 17 ; i ++){14 for( int j = v[i] ; j <= 300 ; j++){15 f[j] += f[j-v[i]];16 }17 }18 }19 intmain(){20

21 intn ;22 Init();23 while( scanf("%d",&n) , n > 0){24 printf("%d\n",f[n]);25 }26 return 0;27 }

Square Coins-完全背包做法

D - 选课时间

题目链接:HDU - 2079

【题意】

给定n门课,每一门课都有相应的学分和数目

请问构成学分为m的方案有多少种?

【母函数做法】

和课堂上例题"整数划分问题"一模一样。

给定“学分”和“数量” 求解某学分的方案数

1 #include

2 using namespacestd;3 const int N = 50;4 intn , m ;5 inta[N] , b[N] , c[N] ;6 intf[N] , tmp[N] ;7 intmain()8 {9 intT ;10 scanf("%d",&T);11 while( T--){12

13 for( int i = 0 ; i < N ; i++ ) tmp[i] = f[i] = 0;14 f[0] = 1;15

16 scanf("%d%d",&n,&m);17

18 for( int i = 1 ; i <= m ; i++){19 scanf("%d%d",&a[i],&b[i]);20 for( int j = 0 ; j <= n ; j++){21 for( int k = 1 ; j + k * a[i] <= n && k <= b[i] ; k++){22 tmp[ j + k*a[i] ] +=f[j];23 }24 }25 for( int j = 0 ; j <= n ; j++){26 f[j] +=tmp[j] ;27 tmp[j] = 0;28 }29 }30 printf("%d\n",f[n]);31 }32 return 0;33 }

选课时间-母函数

【多重背包做法】

给定n个物品,给定物品的“价值”和”数量“

请问构成价值为n的方案数有多少?

1 #include

2 using namespacestd;3 const int N = 50;4 intf[N] ;5 inta[N] , b[N] ;6 intn , m;7 intmain()8 {9 intT;10 scanf("%d",&T);11 while(T--){12 scanf("%d%d",&n,&m);13 for( int i = 0 ; i < m ; i++){14 scanf("%d%d",&a[i],&b[i]);15 }16

17 for( int i = 1 ; i <= n ; i ++ ) f[i] = 0;18 f[0] = 1;19 for( int i = 0 ; i < m ; i ++){20 for( int j = n ; j >= a[i] ; j--){21 for( int k = 1 ; k <= b[i] && j - k*a[i] >= 0 ; k++){22 f[j] += f[j-k*a[i]];23 }24 }25 }26 printf("%d\n",f[n]);27 }28 return 0;29 }

选课时间-多重背包

E - 找单词

题目来源:HDU - 2082

【题意】

给定26个字母的数量,每个字母的权值 按顺序排。

A- 1 , B - 2 …… Z - 26请问由这些数量字母组成的单词价值<=50 的方案数?

【普通型生成函数做法】

根据题目所给定的数量进行构造生成函数。

然后模拟多项式乘法,累加系数[1,50]的方案数。

1 #include

2 #include

3 #include

4 using namespacestd;5 const int N = 30;6 const int M = 50;7 int f[M+10] ;8 int tmp[M+10] ;9 intnum[N];10 intmain()11 {12 intT ;13 scanf("%d",&T);14 while( T--){15 for( int i = 1 ; i <= 26 ; i++ ) scanf("%d",&num[i]);16 for( int i = 0 ; i <= M ; i++ ) f[i] = 0;17 f[0] = 1;18 int Highest_Pow = 0, Limit ;19 for( int i = 1 ; i <= 26 ; i++){20 Limit =min( Highest_Pow , M );21 for( int j = 0 ; j <= Limit ; j++){22 for ( int k = 1 ; k <= num[i] && j + k * i <= M ; k ++){23 tmp[j+k*i] +=f[j];24 }25 }26 Highest_Pow += num[i] *i ;27 Limit =min( Highest_Pow , M );28 for( int j = 0 ; j <= Limit ; j ++){29 f[j] +=tmp[j];30 tmp[j] = 0;31 }32 }33 int ans = 0;34 for( int i = 1 ; i <= M ; i++){35 ans +=f[i] ;36 }37 printf("%d\n",ans);38 }39 return 0;40 }

找单词-母函数

【多重背包做法】

给定物品为数量,价值固定,求出价值之和<=50的方案数

1 #include

2 #include

3 using namespacestd;4 const int N = 55;5 const int M = 50;6 intf[N],num[N];7

8 intmain()9 {10 intT;11 scanf("%d",&T);12 while(T--){13 for( int i = 1 ; i <= 26 ; i ++ ) scanf("%d",&num[i]);14 for( int i = 1 ; i <= M ; i ++ ) f[i] = 0;15

16 f[0] = 1;17 for( int i = 1 ; i <= 26 ; i ++){18 for( int j = M ; j >= i ; j--){19 for( int k = 1 ; k <= num[i] && j - k * i >= 0 ; k ++){20 f[j] += f[j-k*i];21 }22 }23 }24

25 int ans = 0;26 for( int i = 1 ; i <= M ; i ++){27 ans +=f[i];28 }29 printf("%d\n",ans);30 }31 return 0;32 }

找单词-多重背包

F - Crisis of HDU

【题目来源】HDU - 2110

【小结】

这个题意很难读懂。

给定n个物品 的价值及数量

请问 价值为总价值的三分之一 的 方案数?

【题解】

给出物品的价格及数量。

首先判断 总价值是否能被3整除。同时计算完后,如果方案数为0还是输出"sorry"。

然后进行套母函数模板或者多重背包的模板即可

【注意】

取模运算

(a+ b) % mod = a % mod + b %mod

所以过程中进行模运算不影响最后答案在mod意义下的结果

1 #include

2 #include

3 using namespacestd;4

5 const int mod = 10000;6 const int N = 1e3+10;7

8 intf[N],a[N],b[N],tmp[N];9 intn ;10

11 intmain()12 {13 while( scanf("%d",&n) , (n != 0) ){14 int total = 0;15 for( int i = 0 ; i < n ; i ++){16 scanf("%d%d",&a[i],&b[i]);17 total += a[i] *b[i] ;18 }19

20 if( total % 3 != 0){21 printf("sorry\n");22 continue;23 }24

25 int Highest_Pow = 0, Limit ;26 for( int i = 1 ; i <= total / 3 ; i ++ ) f[i] = 0;27 f[0] = 1;28 for( int i = 0 ; i < n ; i ++){29 Limit = min( Highest_Pow , total / 3);30 for( int j = 0 ; j <= Limit ; j ++){31 for( int k = 1 ; k <= b[i] && j + k * a[i] <= total / 3 ; k ++){32 tmp[ j + k*a[i] ] = ( tmp[j+k*a[i]] + f[j] ) %mod ;33 }34 }35

36 Highest_Pow += a[i] *b[i] ;37 Limit = min( Highest_Pow , total / 3);38 for( int j = 0 ; j <= Limit ; j++){39 f[j] = (f[j] + tmp[j]) %mod ;40 tmp[j] = 0;41 }42 }43

44 if( f[ total / 3] ){45 printf("%d\n",f[total/3]);46 }else{47 printf("sorry\n");48 }49 }50 return 0;51 }

Crisis of HDU-母函数

1 #include

2 const int N = 1e3 + 10;3 const int mod =1e4 ;4 intf[N] , a[N] , b[N] ;5

6 intmain()7 {8 intn ;9 while( scanf("%d",&n) , n ){10 int sum = 0;11 for( int i = 0 ; i < n ; i++){12 scanf("%d%d",&a[i],&b[i]);13 sum += a[i] *b[i] ;14 }15 for( int i = 1 ; i <= sum / 3 ; i++ ) f[i] = 0;16 f[0] = 1;17

18 for( int i = 0 ; i < n ; i++){19 for( int j = sum / 3 ; j >= a[i] ; j --){20 for( int k = 1 ; k <= b[i] && j - k * a[i] >= 0 ; k++){21 f[j] = ( f[j] + f[j-k*a[i]] ) %mod;22 }23 }24 }25 if( sum % 3 || ( sum % 3 == 0 && f[sum/3] == 0) ){26 printf("sorry\n");27 }else{28 printf("%d\n",f[sum/3]);29 }30 }31 return 0;32 }

Crisis of HDU-多重背包

G - Fruit

【题目来源】HDU - 2152

【题意】

题目意思是说:价值全为1的物品,然后给出n种不同物品,以及对应的数量。

但是这个数量是一个范围的概念[A,B],即物品的数量必须是[A,B]之间

求出组成价值为m的方案数

【题解】

如果这道题用母函数做,只需要修改题目中所固定的数量即可。

但如果用多重背包来做的话,背包问题默认必须是从某个起始点开始,也就是从0开始进行递推累加方案数。但是如果这个题目的话不能直接套用,但是我们可以技巧性地把问题转移到从0开始。

数量从[A,B]->[0,B-A],这样的话对应的m也需要减去各组物品的A.

然后套用模版即可。

1 #include

2 #include

3 using namespacestd;4 const int N = 2e3 + 10;5 intf[N] , tmp[N] ;6 intmain()7 {8 intn , m , A , B ;9 while( scanf("%d%d",&n,&m) !=EOF ){10 memset( f , 0 , sizeoff );11 scanf("%d%d",&A,&B);12 for( int i = A ; i <= B ; i++ ) f[i] = 1;13 for( int i = 1 ; i < n ; i ++){14 scanf("%d%d",&A,&B);15 for( int j = 0 ; j <= m ; j++){16 for( int k = A ; k <= B && j + k <= m ; k ++){17 tmp[j+k] +=f[j];18 }19 }20 for( int j = 0 ; j <= m ; j++){21 f[j] =tmp[j];22 tmp[j] = 0;23 }24 }25 printf("%d\n",f[m]);26 }27 return 0;28 }

Fruit-母函数

1 #include

2 #include

3 const int N = 205;4 intf[N] , A[N] , B[N];5 intmain()6 {7 intn , m ;8 while( scanf("%d%d",&n,&m) !=EOF){9 memset( f , 0 , sizeoff );10 f[0] = 1;11

12 for( int i = 0 ; i < n ; i++){13 scanf("%d%d",&A[i],&B[i]);14 m -=A[i];15 B[i] -=A[i];16 }17

18 for( int i = 0 ; i < n ; i++){19 for( int j = m ; j >= 1 ; j --){20 for( int k = 1 ; k <= B[i] && j - k >= 0 ; k ++){21 f[j] += f[j-k];22 }23 }24 }25 if( m < 0 ) printf("0\n");26 else printf("%d\n",f[m]);27 }28 return 0;29 }

Fruit-多重背包

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值