1. 程式人生 > >多重揹包二進位制優化(wzk吃小雞腿)

多重揹包二進位制優化(wzk吃小雞腿)

問題 B: WZK吃小雞腿(chicken)

時間限制: 1 Sec   記憶體限制: 128 MB
提交: 53   解決: 23
[ 提交][ 狀態][ 討論版]

題目描述

As is known to all,WZK很能吃小雞腿,但他的胃畢竟有一個最大容納值c,否則胃中小雞腿的巨大引力場和lōng場疊加後會有很可怕的效果。在CZYZ的食堂一共有n種小雞腿有賣,每種小雞腿都有有限的個數、重量和讓WZK感到的滿意度。WZK想在肚子不被撐爆的前提下,取得最大的滿意度值。然後lōng場就得到了大大的power up。

輸入

第一行:兩個正整數n,c。 接著n行,每行三個正整數Ai,Mi,Wi,分別表示每種小雞腿的個數、重量和滿意度。

輸出

一個正整數,表示最大的滿意度。

樣例輸入

3 102 1 31 5 83 3 5

樣例輸出

19

提示

20%的資料ΣAi<=25
另外30%的資料c<=1000, ΣAi<=10000
對於100%的資料c<=10000,n<=100,Ai<=1000

這道題的演算法是很明顯的,多重揹包。但是,普通的多重揹包是三重迴圈的,所以肯定是要超時的(然而因為資料太水,被我三重迴圈水過了)。所以,這裡要用到一個多重揹包問題的常見優化,二進位制優化。我們可以發現,多重揹包的主要時間複雜度多在那一個多出來的k迴圈,也就是那個物品個數的迴圈。如果能把這個迴圈進行優化甚至去掉,那就可以節省大量的時間。而又因為多重揹包的原型就是01揹包,所以想到將其轉換回01揹包。那麼就要將這些物品進行處理。原來的多重揹包,那個k迴圈就相當於把有k個的一種物品分成了k個物品,但是這樣過於浪費時間。所以在將物品進行重新處理的時候,我們 可以用到二進位制的思想。首先,舉個例子:1,2,4,8個物品可以組成1-15任何個數的物品。這一點就是二進位制優化的核心內容。將物品的個數以二進位制的方式進行處理,那麼就可以在減少分的物品個數(這樣子分肯定是要比k要小的)的同時能夠達到1-原來個數之間的任意數。所以,我們在讀入的時候可以對資料一一進行重新處理。如:一種物品有8個 每個物品重量為5  價值為10,那麼進行處理之後就變成個數為1,2,4,1的四個物品,這四個物品的重量分別為5,10,20,5,價值分別為10,20,40,10。這樣就讓這一種物品變成了新的四個物品。將所有資料都進行處理完之後,只要再按01揹包來一個雙重迴圈即可解決。
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
int tot,n,c;
int d[1000],a[11000],m[11000],w[11000],m1[11000],w1[11000],p[11000];
void put(int a0,int m0,int w0)//對資料進行重新處理
{
    int i=1;
    while (a0>d[i])
    {
        a0-=d[i];
        tot=tot+1;
        m1[tot]=d[i]*m0;
        w1[tot]=d[i]*w0;
        i=i+1;
    }
    tot=tot+1;
    m1[tot]=a0*m0;
    w1[tot]=a0*w0;
}
using namespace std;
int main()
{
    tot=0;
    scanf("%d%d",&n,&c);
    d[1]=1;
    for (int i=2;i<=15;i++)//產生二進位制數
     d[i]=d[i-1]*2;
    for (int i=1;i<=n;i++)
     {
        scanf("%d%d%d",&a[i],&m[i],&w[i]);
        put(a[i],m[i],w[i]);
     }
    for (int i=1;i<=tot;i++)//01揹包的dp,注意這裡i要到tot不是n,因為資料處理過了
     for (int j=c;j>=m1[i];j--)
        p[j]=max(p[j],p[j-m1[i]]+w1[i]);
    printf("%d",p[c]);
      
}