应用:
有 种物品,第 件物品的价值为 ,你拿它的数量下限为 ,上限为
那么就可以生产以下母函数:
对其运算结果 , 的系数 即为取总价值为 的方案数
代码实现:
在数学的角度上看,到了每一个 ,只要拿出这个大项中的每一个小项 去乘之前得到的 ,再把 加到 里面就可以了
不过在算法的角度,好吧,也没设么优化,只能暴力了
- 如果是直接开矩阵存的话最省时间,但是空间比较费
- 省点空间就是滚动数组,但是在数组滚回来的时候需要memset滚回来的数组,不过好在我们知道最大项有多大,只要更新到最大的那个就好
代码:
#include<bits\stdc++.h>
using namespace std;
const int N=1000;
int a[N],b[N];//x^i的系数
int v[N],n1[N],n2[N];
int n,P;//物品数,总上限
int main(){
scanf("%d%d",&n,&P);
memset(a,0,sizeof(a));
a[0]=1;
int last=0;
for(int i=1;i<=n;i++)scanf("%d",v+i);
for(int i=1;i<=n;i++)scanf("%d",n1+i);
for(int i=1;i<=n;i++)scanf("%d",n2+i);
for(int i=1;i<=n;i++){
int last2=min(last+n2[i]*v[i],P);//下一次的last
memset(b,0,sizeof(int)*(last2+1));//只清空b[0..last2]
for(int j=n1[i];j<=n2[i]&&j*v[i]<=last2;j++){//取j个i物品
for(int k=0;k<=last&&k+j*v[i]<=last2;k++){//k为取j个i之前的状态
b[k+j*v[i]]+=a[k];
}
}
memcpy(a,b,sizeof(int)*(last2+1));//b赋值给a,只赋值0..last2
last=last2;//更新last
}
}
例题:找单词
原题:hdu 2083
题意:
26个字母各a[i]个,a的价值为1,z为26,求有多少种选择方式(无顺序)组成的单词价值少于等于50
解析:
模板的不能再模板了吧,巧的是,我之前不知道母函数的时候用的是多重背包,现在感觉代码都差不多
代码:
#include<bits\stdc++.h>
using namespace std;
const int N=10000;
int a[N],b[N];//x^i的系数
int v[30],n1[30],n2[30];
int n,P;//物品数,总上限
int main(){
int t;scanf("%d",&t);
while(t--){
n=26,P=50;
memset(a,0,sizeof(a));
a[0]=1;
int last=0;
for(int i=1;i<=26;i++)v[i]=i;
for(int i=1;i<=n;i++)n1[i]=0;
for(int i=1;i<=n;i++)scanf("%d",&n2[i]);
for(int i=1;i<=n;i++){
int last2=min(last+n2[i]*v[i],P);//下一次的last
memset(b,0,sizeof(int)*(last2+1));//只清空b[0..last2]
for(int j=n1[i];j<=n2[i]&&j*v[i]<=last2;j++){//取j个i物品
for(int k=0;k<=last&&k+j*v[i]<=last2;k++){//k为取j个i之前的状态
b[k+j*v[i]]+=a[k];
}
}
memcpy(a,b,sizeof(int)*(last2+1));//b赋值给a,只赋值0..last2
last=last2;//更新last
}
int ans=0;for(int i=1;i<=50;i++)ans+=a[i];
printf("%d\n",ans);
}
}