洛谷P1064 金明的預算方案 DP揹包之依賴揹包
今天學習了揹包九講,收益頗多,總算明白了01揹包和完全揹包遍歷順序的區別,依賴揹包怎麼轉化為分組揹包,泛化物品是如何將抽象思維體現的淋漓盡致……
並記住了一句名言:失敗並不是什麼丟人的事,從失敗中全無收穫才是。
開始正題-------金明的預算的方案
題目描述
金明今天很開心,家裡購置的新房就要領鑰匙了,新房裡有一間金明自己專用的很寬敞的房間。更讓他高興的是,媽媽昨天對他說:“你的房間需要購買哪些物品,怎麼佈置,你說了算,只要不超過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)。
分析
依賴揹包問題可以對每個主件的附件進行01揹包,得到主件的個數個物品組,組內元素代表的是對於每個主件的選取策略,對於每個物品組只能選擇組內的一種策略,故可轉化為分組揹包問題來求解。
但是此題由於每個主件規定最多隻有兩個附件,所以直接列舉那四種情況即可(只選主件,主件+附件1,主件+附件2,都選)
具體細節見程式碼:(這裡採用連結串列的形式儲存主件附件之間的依賴關係,便於應對多附件的情況)
#include <iostream>
#include <cstdio>
using namespace std;
struct thing
{
int v;
int p;
int q;
struct thing *next;
}th[61];
int n,m;
int f[33000];//一維陣列優化。表示當可用費用為i時的最大價值
void pack()
{
int i,j;
for(i=1;i<=m;++i)
{
if(th[i].q>0)//附件直接跳過,因為不能只選附件
continue;
for(j=n;j>=0;--j)
{
struct thing *pi=th[i].next,*pj=NULL;//兩個指標分別代表兩個附件
if(pi!=NULL)
pj=pi->next;
if(j>=th[i].v)//只選主件
f[j]=max(f[j],f[j-th[i].v]+th[i].p);
if(pi!=NULL&&j-th[i].v-pi->v>=0)//選主件和附件1
f[j]=max(f[j],f[j-th[i].v-pi->v]+th[i].p+pi->p);
if(pj!=NULL&&j-th[i].v-pj->v>=0)//選主件和附件2
f[j]=max(f[j],f[j-th[i].v-pj->v]+th[i].p+pj->p);
if(pi!=NULL&&pj!=NULL&&j-th[i].v-pi->v-pj->v>=0)//都選
f[j]=max(f[j],f[j-th[i].v-pi->v-pj->v]+th[i].p+pi->p+pj->p);
}
}
}
void scanff()
{
cin>>n>>m;//最大費用n,物品m個
int i;
for(i=1;i<=m;++i)
{
th[i].next=NULL;
cin>>th[i].v>>th[i].p>>th[i].q;
th[i].p*=th[i].v;//定義價值
if(th[i].q!=0) //規範依賴關係
{
th[i].next=th[th[i].q].next;
th[th[i].q].next=&th[i];
}
}
}
int main()
{
scanff();
pack();
cout<<f[n]<<endl;
return 0;
}