HDU 2152 Fruit (母函式+模板)
阿新 • • 發佈:2019-02-14
轉眼到了收穫的季節,由於有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
Sample Output
於是,很多人們慕名而來,找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
2 12
題解:
比賽的時候用dfs+剪枝+二分tle了無數遍的一道題。。比完賽才知道有一種東西叫母函式,是一個模板。。然後就百度啊百度,沒看懂。。問同學說只要搜一個模板套的去就好了。。真是神奇的一題,筆記筆記
以下是搜到的內容:
附上神犇部落格講解:http://blog.csdn.net/xiaofei_it/article/details/17042651 本題講解:http://blog.csdn.net/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;
}