「NOIP2006」「LuoguP1064」 金明的預算方案(分組揹包
題目描述
金明今天很開心,家裡購置的新房就要領鑰匙了,新房裡有一間金明自己專用的很寬敞的房間。更讓他高興的是,媽媽昨天對他說:“你的房間需要購買哪些物品,怎麼佈置,你說了算,只要不超過NNN元錢就行”。今天一早,金明就開始做預算了,他把想買的物品分為兩類:主件與附件,附件是從屬於某個主件的,下表就是一些主件與附件的例子:
主件 附件
電腦 印表機,掃描器
書櫃 圖書
書桌 檯燈,文具
工作椅 無
如果要買歸類為附件的物品,必須先買該附件所屬的主件。每個主件可以有000個、111個或222個附件。附件不再有從屬於自己的附件。金明想買的東西很多,肯定會超過媽媽限定的NNN元。於是,他把每件物品規定了一個重要度,分為555等:用整數1−51-51−5表示,第555等最重要。他還從因特網上查到了每件物品的價格(都是101010元的整數倍)。他希望在不超過NNN元(可以等於NNN元)的前提下,使每件物品的價格與重要度的乘積的總和最大。
設第jjj件物品的價格為v[j]v_[j]v[j],重要度為w[j]w_[j]w[j],共選中了kkk件物品,編號依次為j1,j2,…,jkj_1,j_2,…,j_kj1,j2,…,jk,則所求的總和為:
v[j1]×w[j1]+v[j2]×w[j2]+…+v[jk]×w[jk]v_[j_1] \times w_[j_1]+v_[j_2] \times w_[j_2]+ …+v_[j_k] \times w_[j_k]v[j1]×w[j1]+v[j2]×w[j2]+…+v[jk]×w[jk]。
請你幫助金明設計一個滿足要求的購物單。
輸入輸出格式
第111行,為兩個正整數,用一個空格隔開:
NmN mNm (其中N(<32000)N(<32000)N(<32000)表示總錢數,m(<60)m(<60)m(<60)為希望購買物品的個數。) 從第222行到第m+1m+1m+1行,第jjj行給出了編號為j−1j-1j−1的物品的基本資料,每行有333個非負整數
vpqv p qvpq (其中vvv表示該物品的價格(v<10000v<10000v<10000),p表示該物品的重要度(1−51-51−5),qqq表示該物品是主件還是附件。如果q=0q=0q=0,表示該物品為主件,如果q>0q>0q>0,表示該物品為附件,qqq是所屬主件的編號)
一個正整數,為不超過總錢數的物品的價格與重要度乘積的總和的最大值(<200000<200000<200000)。
輸入輸出樣例
輸入樣例#1: 複製1000 5 800 2 0 400 5 1 300 5 1 400 3 0 500 2 0輸出樣例#1: 複製
2200
說明
NOIP 2006 提高組 第二題
題解
算是分組揹包的模板題了叭?
設主件為1,附件為2,3,
那麼將${1},{1,2},{1,3},{1,2,3}$視為同組內的物件,同組內至多隻能選1個。
//如果只有一個附件,組內就是${1},{1,2}$;如果沒有附件,組內就是${1}$。
跑分組揹包就行了。
關於分組揹包:
因為和01一樣只有選和不選的區別,不像完全揹包可以無限選,
所以容積也是要從大到小迴圈。
然後在一組裡面只能選一個,所以乾脆一組一組的跑,
在每一組裡面迴圈容積,在容積裡面再迴圈每件物品,就可以保證是由不含該組物品的狀態轉移而來了。
1 /* 2 qwerta 3 P1064 金明的預算方案 4 Accepted 5 100 6 程式碼 C++,1.19KB 7 提交時間 2018-10-17 09:46:49 8 耗時/記憶體 9 49ms, 932KB 10 */ 11 #include<iostream> 12 #include<cstdio> 13 using namespace std; 14 struct emm{ 15 int w,v,f,lson,rson; 16 }a[63];//w:費用 v:價值 f:父節點 17 struct ahh{ 18 int w,v; 19 }b[7];//分組用的 20 int f[32003];//dp陣列 21 int main() 22 { 23 //freopen("a.in","r",stdin); 24 int n,m; 25 scanf("%d%d",&n,&m); 26 for(int i=1;i<=m;++i) 27 { 28 int v,p,fa; 29 scanf("%d%d%d",&v,&p,&fa); 30 a[i].w=v; 31 a[i].v=v*p; 32 a[i].f=fa; 33 if(fa) 34 { 35 if(!a[fa].lson) 36 a[fa].lson=i; 37 else 38 a[fa].rson=i; 39 } 40 } 41 for(int k=1;k<=m;++k)//列舉每組 42 if(!a[k].f)//如果該件為主件(代表一個組) 43 { 44 int kk=0; 45 //分組,存在b裡 46 b[++kk]=(ahh){a[k].w,a[k].v};//1 47 if(a[k].lson) 48 { 49 b[++kk]=(ahh){a[k].w+a[a[k].lson].w,a[k].v+a[a[k].lson].v};//1,2 50 if(a[k].rson) 51 { 52 b[++kk]=(ahh){a[k].w+a[a[k].rson].w,a[k].v+a[a[k].rson].v};//1,3 53 b[++kk]=(ahh){a[k].w+a[a[k].lson].w+a[a[k].rson].w 54 ,a[k].v+a[a[k].lson].v+a[a[k].rson].v};//1,2,3 55 } 56 } 57 //cout<<k<<" "<<kk<<endl; 58 for(int v=n;v;--v)//從大到小列舉容積 59 for(int i=1;i<=kk;++i)//迴圈組內元素 60 if(v-b[i].w>=0) 61 f[v]=max(f[v],f[v-b[i].w]+b[i].v); 62 } 63 cout<<f[n];//輸出 64 return 0; 65 }