1. 程式人生 > >「NOIP2006」「LuoguP1064」 金明的預算方案(分組揹包

「NOIP2006」「LuoguP1064」 金明的預算方案(分組揹包

題目描述

金明今天很開心,家裡購置的新房就要領鑰匙了,新房裡有一間金明自己專用的很寬敞的房間。更讓他高興的是,媽媽昨天對他說:“你的房間需要購買哪些物品,怎麼佈置,你說了算,只要不超過NNN元錢就行”。今天一早,金明就開始做預算了,他把想買的物品分為兩類:主件與附件,附件是從屬於某個主件的,下表就是一些主件與附件的例子:

主件 附件

電腦 印表機,掃描器

書櫃 圖書

書桌 檯燈,文具

工作椅 無

如果要買歸類為附件的物品,必須先買該附件所屬的主件。每個主件可以有000個、111個或222個附件。附件不再有從屬於自己的附件。金明想買的東西很多,肯定會超過媽媽限定的NNN元。於是,他把每件物品規定了一個重要度,分為555等:用整數1−51-515表示,第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-1j1的物品的基本資料,每行有333個非負整數

vpqv p qvpq (其中vvv表示該物品的價格(v<10000v<10000v<10000),p表示該物品的重要度(1−51-515),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 }