ZOJ Cookie Choice 多重揹包 單調隊列優化 分組揹包 泛化物品求和

題意:一個人想買甜點。有N種不同的甜點,每種甜點有價值Ei,價格Pi。這個人對每種甜點想買Ki塊。當Ki =0時,該甜點可以買無限塊。

           同時,因爲有些甜點的味道相同,他將有些甜點分成了G組。每組內的甜點,最多買一種。

           現在他有D元錢,想把這些錢全花光,而且最後得到的價值最大且非負,問是否有對應的方案,如果有,求出最大的價值。

思路:不同類型的揹包的大拼題。

           對於不同的揹包類型,我們分別處理就行了。

           因爲這裏的Ki,D都比較大,用二進制分解的好像就有點慢了。對於這樣的最優性的多重揹包,我們可以考慮用單調隊列來進行優化。

           對於每一組內的東西,因爲最多可以買一種,所以我們可以對每種甜點進行對應的揹包,然後取最大值,得到這個組的泛化物品的價值。

           最後,就是對多件泛化物品進行合併了。對應的方程是 dp[j] = max(dp[j],dp[j-w] +v[i][k]) 0 <= w <= j

           這個時候要注意循環的順序了。因爲是用的滾動數組,我們必須要保證更新dp[j]的時候,其他值是i-1物品對應的值,否則就變成了完全揹包,累加錯誤。

代碼如下:

#include <cstdio>
#include <algorithm>
#include <cstring>
#include <vector>

using namespace std;

const int MAX = 1500;

int N,D,G;
int k[MAX],e[MAX],p[MAX];
int dp1[MAX],dp2[10][MAX],dp3[MAX];
int g[10][MAX],sz[10],deq[MAX][2];
bool used[MAX];

void multi_package(int * dp,int i)
{
    for(int a = 0; a < p[i]; ++a){
        int h = 0, t = 0;
        for(int j = 0; j * p[i] + a <= D; ++j){
            int val = dp[j * p[i] + a] - j * e[i];
            while(h < t && deq[h][1] <= val) t--;
            deq[t][0] = j,deq[t++][1] = val;
            dp[j * p[i] + a] = deq[h][1] + j * e[i];
            if(deq[h][0] == j - k[i]) h++;
        }
    }
}

void complete_package(int * dp, int i)
{
    for(int j = p[i]; j <= D; ++j)
        dp[j] = max(dp[j],dp[j-p[i]] + e[i]);
}

int main(void)
{
    //freopen("input.txt","r",stdin);
    while(scanf("%d%d",&N,&D) != EOF){
        memset(used,0,sizeof(used));
        memset(sz,0,sizeof(sz));
        for(int i = 0; i < N; ++i)
            scanf("%d%d%d",&k[i],&e[i],&p[i]);
        scanf("%d",&G);
        for(int i = 0 ; i < G; ++i){
            int d; char ch;
            while(scanf("%d%c",&d,&ch)){
                g[i][sz[i]++] = --d;
                used[d] = true;
                if(ch == '\n')
                    break;
            }
        }
        memset(dp1,0xcf,sizeof(dp1));
        dp1[0] = 0;
        for(int i = 0; i < N; ++i){
            if(used[i]) continue;
            if(k[i])
                multi_package(dp1,i);
            else
                complete_package(dp1,i);
        }
        memset(dp2,0xcf,sizeof(dp2));
        for(int id = 0; id < G; ++id){
            for(int i = 0; i < sz[id]; ++i){
                memset(dp3,0xcf,sizeof(dp3));
                dp3[0] = 0;
                if(k[g[id][i]])
                    multi_package(dp3,g[id][i]);
                else
                    complete_package(dp3,g[id][i]);
                for(int i = 0; i <= D; ++i)
                    dp2[id][i] = max(dp2[id][i],dp3[i]);
            }
        }
        for(int id = 0; id < G; ++id)
            for(int j = D; j >= 0; --j)
                for(int w = 0; w <= j; ++w)
                    dp1[j] = max(dp1[j],dp1[j-w] + dp2[id][w]);
        if(dp1[D] < 0)
            puts("i'm sorry...");
        else
            printf("%d\n",dp1[D]);
    }
    return 0;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章