组合——普通母函数


一、母函数

1.结构

对于序列 a 0 a_0 a0 a 1 a_1 a1…… a k a_k ak构造一个函数G(x),
G(x) = a 0 a_0 a0 + a 1 a_1 a1x + a 2 a_2 a2 x 2 x^2 x2 +…… a k a_k ak* x k x^k xk
则称G(x)为序列 a 0 a_0 a0 a 1 a_1 a1…… a k a_k ak的母函数

2.思想

把组合问题的加法法则和幂级数的的乘幂的相加对应起来.
把每种物品的所有情况枚举出来,并以幂函数的形式表示出来,将这个幂函数作为多项式里面的一个项,最后将多项式展开的结果就反应了各种组合的结果。

这种方法的复杂度为 O( n 3 n^3 n3


二、个数有限,求组合可能

硬币问题,个数有限求可表示数值

硬币问题

有面值为1、2、5的硬币,各自有1、1、3枚,求可以组合的可能。
对于一个组合的结果来说,其实是由三种硬币的不同的不同取法。
第一种可以取0、1;
第二种可以取0、1、2;
第三种可以取0、1、2、3、4、5;
使用母函数,处理问题:
指数意味着对该种物品的价值。
x 0 x^0 x0+ x 1 x^1 x1) ( x 0 x^0 x0+ x 2 x^2 x2+ x 4 x^4 x4) ( x 0 x^0 x0+ x 5 x^5 x5+ x 10 x^{10} x10+ x 15 x^{15} x15+ x 20 x^{20} x20+ x 25 x^{25} x25
将多项式展开就可以得到不同价值以及对应价值组合的方案数。

#include<bits/stdc++.h>
using namespace std;
const int N = 10000;
int c1[N];
int c2[N];
int main(){
    int a[3];
    int num[3]={1,2,5};
    while(cin>>a[0]>>a[1]>>a[2]){
        memset(c1,0,sizeof(c1));
        if(a[0]==0&&a[1]==0&&a[2]==0)
            continue;
        c1[0]=1;
        int maxx=0;
        for(int i=0;i<3;i++){//每个因子循环
            maxx+=a[i]*num[i];
            for(int j=0;j<=maxx;j++){//因子内每一个项循环
                for(int k=0;k<=num[i]*a[i]&&k+j<=maxx;k+=num[i]){//与另一个式子相乘循环
                    c2[k+j]+=c1[j];//将左括号内的值化入右括号中 ,形成一个指数范围到maxx的括号 
                }
            }
        memcpy(c1,c2,sizeof(c2));
        memset(c2,0,sizeof(c2));
        }
        int i=0;
        while(c1[i])
               i++;
        printf("%d\n",i);
    }
    return 0;
}

三、个数不限,求方案数

整数划分
此时会先给定目标 n ,求组合成目标的方案数。
第 i 个物品价值为 v[i] ,最多由 n / v [ i ] n/v[i] n/v[i]个物品组成,所以仍然可以把其化为上面那种问题。
然后思路相同。

#include<bits/stdc++.h>
using namespace std;
const int N=10000;
int c1[N+1],c2[N+1];
int main(){
	int n;
	while(cin>>n){
		for(int i=0;i<=n;i++){
			c1[i]=c2[i]=0;
		}
		for(int i=0;i<=n;i++){
			c1[i]=1;			
		}
		for(int i=2;i<=n;i++){
			for(int j=0;j<=n;j++)
				for(int k=0;k+j<=n;k+=i){
					c2[j+k]+=c1[j];
				}
			for(int j=0;j<=n;j++){
				c1[j]=c2[j];
				c2[j]=0;
			}
		}
		cout<<c1[n]<<endl;
	}
}

四、代码模板

思路

把括号从前往后处理,逐步化去。

#include<bits/stdc++.h>
using namespace std;
const int N=10000;
int c1[N+1],c2[N+1];
int main(){
	int n;
	while(cin>>n){
		for(int i=0;i<=n;i++){
			c1[i]=c2[i]=0;
		}
		for(int i=0;i<=n;i++){
			c1[i]=1;			
		}
		for(int i=2;i<=n;i++){
			for(int j=0;j<=n;j++)
				for(int k=0;k+j<=n;k+=i){
					c2[j+k]+=c1[j];
				}
			for(int j=0;j<=n;j++){
				c1[j]=c2[j];
				c2[j]=0;
			}
		}
		cout<<c1[n]<<endl;
	}
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值