上一篇文章讲了母函数形式上的意义和构造方法
下面两篇文章讲一些代码实现和例子。
1.有限个物品的通解
for(int i=0;i<n;i++)
{
for(int j=0;j<maxn;j++)
for(int k=0;k<=a[i];k++)
c2[j+value[i]*k]+=c1[j];//c2[j+value[i]*k]+=c1[j]/ji(k); ji为预处理的阶乘
memcpy(c1,c2,sizeof(c1));
memset(c2,0,sizeof(c2));
}
乍一看,不就很类似背包DP(应该是多重背包)的过程吗?
①value[i]代表各自物品对所求答案的贡献,通常为质量,对应背包各种物品的质量。
②j是当前多项式的次数(已经取好的物品总量),对应背包剩余容量。
③k是当前要取的当前物品个数(最大为这种物品的个数,对应最高次数)
④+=因为有可能有不同方法得到同一个项 比如,
有两种途径得到。
⑤至少需要两个数组,一个是c1存储答案,一个是c2存储当前下一个乘积得到的多项式系数
类似归并排序的思想,每次都要把c2复制到c1,c2清空
可能用到value数组,但如果价值都为1可以省略。
2.指数型母函数 HDU1521 排列组合
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
double a[130],b[130];//a 保存结果 b临时存
int num[130];
int jc(int p)
{
int cnt=1;
for(int i=2;i<=p;i++)cnt*=i;
return cnt;
}
int main()
{
int x,y;
while(cin>>x>>y)
{
for(int i=1;i<=x;i++)cin>>num[i];
memset(a,0,sizeof(a));
memset(b,0,sizeof(b));
a[0]=1;
for(int i=1;i<=x;i++)//取第几个
{
for(int j=0;j<=y;j++)//已经乘了的指数
{
for(int k=0;(k*1+j<=y)&&(k<=num[i]);k++)//每个物品对排列组合数贡献为1
{
b[k+j]+=a[j]/jc(k);//指数型母函数,需要多除以一个k!
}
}
for(int j=0;j<=y;j++)
{
a[j]=b[j];
b[j]=0;
}
}
printf("%.0lf\n",a[y]*jc(y));//不能直接cout,会WA
}
return 0;
}
3.普通型母函数+value带权值 HDU2082 找单词
每个单词有权值,比如A权值为1,B为2... 注意此时第三层for循环,k应该带系数
求总和而不是单种单词的总和,所以最后要for循环加一遍
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int a[60],b[60];//a 保存结果 b临时存
int main()
{
int n;
cin>>n;
while(n--)
{
memset(a,0,sizeof(a));
memset(b,0,sizeof(b));
a[0]=1;
int x;
for(int i=1;i<=26;i++)
{
scanf("%d",&x);
for(int j=0;j<=50;j++)//已经求好的指数
{
for(int k=0;k<=x&&(j+k*i<=50);k++)//每种单词带的指数
{
b[i*k+j]+=a[j];//这里k前面就要乘了
}
}
for(int j=0;j<=50;j++)
{
a[j]=b[j];
b[j]=0;
}
}
ll ans=0;
for(int i=1;i<=50;i++)
{
ans+=a[i];
}
cout<<ans<<endl;
}
return 0;
}