HDU 2152 Fruit (母函数+模板)

转眼到了收获的季节,由于有TT的专业指导,Lele获得了大丰收。特别是水果,Lele一共种了N种水果,有苹果,梨子,香蕉,西瓜……不但味道好吃,样子更是好看。 

于是,很多人们慕名而来,找Lele买水果。 

甚至连大名鼎鼎的HDU ACM总教头 lcy 也来了。lcy抛出一打百元大钞,"我要买由M个水果组成的水果拼盘,不过我有个小小的要求,对于每种水果,个数上我有限制,既不能少于某个特定值,也不能大于某个特定值。而且我不要两份一样的拼盘。你随意搭配,你能组出多少种不同的方案,我就买多少份!" 

现在就请你帮帮Lele,帮他算一算到底能够卖出多少份水果拼盘给lcy了。 

注意,水果是以个为基本单位,不能够再分。对于两种方案,如果各种水果的数目都相同,则认为这两种方案是相同的。 

最终Lele拿了这笔钱,又可以继续他的学业了~ 
Input
本题目包含多组测试,请处理到文件结束(EOF)。 
每组测试第一行包括两个正整数N和M(含义见题目描述,0<N,M<=100) 
接下来有N行水果的信息,每行两个整数A,B(0<=A<=B<=100),表示至少要买该水果A个,至多只能买该水果B个。 
Output
对于每组测试,在一行里输出总共能够卖的方案数。 
题目数据保证这个答案小于10^9 
Sample Input
2 3
1 2
1 2
3 5
0 3
0 3
0 3
Sample Output
2
12

题解:

比赛的时候用dfs+剪枝+二分tle了无数遍的一道题。。比完赛才知道有一种东西叫母函数,是一个模板。。然后就百度啊百度,没看懂。。问同学说只要搜一个模板套的去就好了。。真是神奇的一题,笔记笔记

以下是搜到的内容:

附上神犇博客讲解:http://blog.youkuaiyun.com/xiaofei_it/article/details/17042651   本题讲解:http://blog.youkuaiyun.com/xiaofei_it/article/details/17042497

然后是要笔记的内容:

通用模板:

//为计算结果,b为中间结果。
int a[MAX],b[MAX];
//初始化a
memset(a,0,sizeof(a));
a[0]=1;
for (int i=1;i<=17;i++)//循环每个因子
{
	memset(b,0,sizeof(b));
	for (int j=n1[i];j<=n2[i]&&j*v[i]<=P;j++)//循环每个因子的每一项
		for (int k=0;k+j*v[i]<=P;k++)//循环a的每个项
			b[k+j*v[i]]+=a[k];//把结果加到对应位
	memcpy(a,b,sizeof(b));//b赋值给a
}
讲解:

K对应具体问题中物品的种类数。

v[i]表示该乘积表达式第i个因子的权重,对应于具体问题的每个物品的价值或者权重。

n1[i]表示该乘积表达式第i个因子的起始系数,对应于具体问题中的每个物品的最少个数,即最少要取多少个。

n2[i]表示该乘积表达式第i个因子的终止系数,对应于具体问题中的每个物品的最多个数,即最多要取多少个。

P是可能的最大指数。拿钞票组合这题来说,如果要求15元有多少组合,那么P就是15;如果问最小的不能拼出的数值,那么P就是所有钱加起来的和

如果n2是无穷,那么第二层循环条件j<=n2[i]可以去掉。


最后结果为a[p];


如何提高效率?

用一个last变量记录目前最大的指数,这样只需要在0..last上进行计算。

这里给出第二个模板:

//初始化a,因为有last,所以这里无需初始化其他位
a[0]=1;
int last=0;
for (int i=0;i<K;i++)
{
	int last2=min(last+n[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++)//这里是last2
		for (int k=0;k<=last&&k+j*v[i]<=last2;k++)//这里一个是last,一个是last2
			b[k+j*v[i]]+=a[k];
	memcpy(a,b,sizeof(int)*(last2+1));//b赋值给a,只赋值0..last2
	last=last2;//更新last
}

详情请看神犇博客qaq我只是会套
这题我的代码:

#include<iostream>
#include<stdio.h>
#include<string>
#include<cstring>
#include<map>
#include<queue>
#include<stack>
#include<algorithm>
#include<math.h>
#include<deque>
#include<stack>
using namespace std;
int n1[105];
int n2[105];
int a[205];
int b[205];
int main()
{
    int m,n,i,j,k;
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        for(i=0;i<n;i++)
            scanf("%d%d",&n1[i],&n2[i]);
        memset(a,0,sizeof(a));
        a[0]=1;
        for(i=0;i<n;i++)//循环每个因子
        {
            memset(b,0,sizeof(b));
            for(j=n1[i];j<=n2[i]&&j<=m;j++)//循环每个因子的每一项
                for(k=0;k+j<=m;k++)//循环a的每个项
                    b[k+j]+=a[k];//把结果加到对应位
            memcpy(a,b,sizeof(b));//b赋值给a
        }
        printf("%d\n",a[m]);
    }
    return 0;
}









评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值