1. 程式人生 > >HDU 2152 Fruit (母函式+模板)

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.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;
}