母函数
来源:互联网 发布:python高级编程 豆瓣 编辑:程序博客网 时间:2024/06/10 20:01
母函数即生成函数,有普通型生成函数和指数型生成函数两种,常用于组合数学中求某个问题的方法数。
例:G(x) = a0 + a1x + a2x*2 + a3x^3 +....+ anx^n
其中ai表示i的组合数
应用例如:
有1克、2克、3克、4克的砝码各一枚,能称出哪几种重量?每种重量各有几种可能方案?
(1+x)(1+x2)(1+x3)(1+x4)
=(1+x+x2+x3)(1+x3+x4+x7)
=1+x+x2+2x3+2x4+2x5+2x6+2x7+x8+x9+x10
所谓整数拆分即把整数分解成若干整数的和(相当于把n个无区别的球放到n个无标志的盒子,盒子允许空,也允许放多于一个球)。
整数拆分成若干整数的和,办法不一,不同拆分法的总数叫做拆分数。
hdu 1028 Ignatius and the Princess III
http://acm.hdu.edu.cn/showproblem.php?pid=1028
给定一个的数,拆分成由(1, 2, 3.。。。。。。)中任意几个所组成的,问有多少种拆分方法~
#include <iostream>#include <cstdio>#include <cstring>using namespace std;const int maxn= 130;int n;int c1[maxn], c2[maxn];void handle(){ int i, j, k, t; memset( c2, 0, sizeof( c2)); for( i=0; i<=n; i++)//第一项 即(1+ x + x2 + x3 +……) c1[i]= 1; for( i=2; i<=n; i++){ //枚举2到n项 for( j=0; j<=n; j++)//枚举每一位系数,即所求系数 for( k=0; k+j<=n; k+=i)//枚举每一项中的各位,k+=i~~~ c2[j+k]+= c1[j]; for( j=0; j<=n; j++){ c1[j]= c2[j]; c2[j]= 0; } }}int main(){ // freopen("1,txt", "r", stdin); while( scanf("%d", &n) != EOF){ handle(); printf("%d\n", c1[n]); } return 0;}
Square CoinsSquare Coins hdu 1398
http://acm.hdu.edu.cn/showproblem.php?pid=1398
钱币的价值为1^2, 2^2......17^2,问是否组合可得某价值
#include <iostream>#include <cstdio>#include <cmath>#include <queue>#include <cstring>using namespace std;const int maxn= 500;int c1[maxn], c2[maxn];int w[20];int n;void handle(){ int i, j, k, t; memset( c2, 0, sizeof( c2)); for( i=0; i<=n; i++) c1[i]= 1; for( i=2; i<=17; i++){ for( j=0; j<=n; j++) for( k=0; k+j<=n; k+= w[i]) c2[j+k]+= c1[j]; for( j=0; j<=n; j++){ c1[j]= c2[j]; c2[j]= 0; } }}int main(){ // freopen("1.txt", "r", stdin); int i; for(i=1; i<=17; i++) w[i]= i*i; while( scanf("%d", &n) && n){ handle(); printf("%d\n", c1[n]); } return 0;}
hdu 1085 Holding Bin-Laden Captive!
http://acm.hdu.edu.cn/showproblem.php?pid=1085
此题背包也可,或直接计算器特殊情况
有1,2,5三种面值的钱币,各有一定数量,问最小的不能得到的组合值是多少
#include <iostream>#include <cstdio>#include <cmath>#include <queue>#include <cstring>using namespace std;const int maxn= 1000000;int c1[maxn], c2[maxn], a[3], v[3]={1, 2, 5};int n;void handle(int cnt){ int i, j, k, t; memset( c1, 0, sizeof( c1)); memset( c2, 0, sizeof( c2)); for( i=0; i<= a[0]; i++) c1[i]= 1; for( i=1; i<=2; i++){ for( j=0; j<=cnt ; j++){ for( k=0, t= 0; k+j <= cnt && t<= a[i]; k+= v[i], t++) c2[j+k]+= c1[j]; } for( j= 0; j<= cnt; j++){ c1[j]= c2[j]; c2[j]= 0; } }}int main(){ // freopen("1.txt", "r", stdin); int i; while( scanf("%d%d%d", &a[0], &a[1], &a[2]) && !( a[0]==0 && a[1]==0 && a[2]==0)){ n= a[0] + v[1]*a[1] + v[2]*a[2]; handle( n+1); for( i=1; i<=n+1; i++) if( c1[i]== 0){ printf("%d\n", i); break; } } return 0;}
hdu 1059 Dividing
http://acm.hdu.edu.cn/showproblem.php?pid=1059
同样是可以有分组背包做
有1~6种价值的物品,分别有一定数量,问可否平分
计算ans/2系数是否为0
主要对ans/2直接处理会超时,由于60为1, 2, 3, 4, 5 ,6的最小公倍数,所以ans对60取余后,再对ans/2处理
#include <iostream>#include <cstdio>#include <cmath>#include <queue>#include <cstring>using namespace std;const int maxn= 60005;int c1[maxn], c2[maxn];int n, w[10]={1, 2, 3, 4, 5, 6};int num[10], ans;void handle(){ int i, j, k; memset( c2, 0, sizeof( c2)); memset( c1, 0, sizeof( c1)); for( i=0; i<=ans/2 && i<=num[0]; i++) //注意可取个数 c1[i]= 1; for( i=1; i<=5; i++){ //枚举有多少项, 1+x1+x2+x3... 为一项 for( j=0; j<=ans/2; j++) //j数组存储为每一位的系数,下标为权值,即x0, x1, x2。。。时的方法数 for( k=0; k*w[i]+j<=ans/2 && k<=num[i]; k++) //这里注意,k表示的是数量,不要与权值弄混 c2[k*w[i]+j]+= c1[j]; //c的下标为权值,故用k*w[i],存储值为方法数 for( j=0; j<=ans/2; j++){ c1[j]= c2[j]; c2[j]= 0; } }}int main(){ //freopen("1.txt", "r", stdin); int i, j, k, text=1; while( scanf("%d%d%d%d%d%d", &num[0], &num[1], &num[2], &num[3], &num[4], &num[5]) && !(num[0]==0 && num[1]==0 && num[2]==0 && num[3]==0 && num[4]==0 && num[5]==0)){ printf("Collection #%d:\n", text++); for( i=0, ans= 0; i<=5; i++) ans+= w[i] * num[i]; if( ans%2 != 0) { printf("Can't be divided.\n\n"); continue; } ans%= 60; handle(); if( c1[ans/2]!= 0) printf("Can be divided.\n\n"); else printf("Can't be divided.\n\n"); } return 0;}