母函数运用

应用:

n 种物品,第 i 件物品的价值为 v i ,你拿它的数量下限为 n 1 i ,上限为 n 2 i

那么就可以生产以下母函数:

i = 1 n ( x v i n 1 i + x v i ( n 1 i + 1 ) + . . . + x v i ( n 2 i 1 ) + x v i n 2 i )

对其运算结果 b 0 x 0 + b 1 x 1 + . . . + b m a x x m a x , x i 的系数 b i 即为取总价值为 i 的方案数


代码实现:

在数学的角度上看,到了每一个 i ,只要拿出这个大项中的每一个小项 a x j 去乘之前得到的 b x k ,再把 a b 加到 x j + k 里面就可以了

不过在算法的角度,好吧,也没设么优化,只能暴力了

  • 如果是直接开矩阵存的话最省时间,但是空间比较费
  • 省点空间就是滚动数组,但是在数组滚回来的时候需要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);
}
}


猜你喜欢

转载自blog.csdn.net/jk_chen_acmer/article/details/81783173
今日推荐