題意:一個人想買甜點。有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;
}