1. 程式人生 > >洛谷P1064 金明的預算方案 DP揹包之依賴揹包

洛谷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;
}