题意:一个人想买甜点。有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;
}