Zoj 3164 Cookie Choice(多重背包+分组背包,更新队列优化)

Cookie Choice

Time Limit: 2 Seconds       Memory Limit: 32768 KB

MM enjoyed cookies very much. On Saint Valentine's Day, when she stepped into a big cookie store again, she wouldn't leave unless DD spent all his money in pocket!

There are N kinds of cookies, labeled from 1 to N, and all can be bought without any restriction by the store. But actually, for some kinds of cookies, MM wanted to buy one piece at most, and for some kinds of cookies, MM wanted to buy Ki pieces at most, and for some other kinds of cookies, there didn't exist an upper bound that MM wanted to buy.

There is another requirement from MM: there are some groups of cookies, MM considered their tastes similar, so she wanted to buy at most one kind of cookies in each group. A kind of cookie wouldn't appear in more than one group.

For the ith kind of cookies, MM has an "enjoyable value" Ei, if DD bought Ai pieces of this kind for her, and Ai didn't exceed her upper bound, MM get EiAi of enjoyable value. After buying cookies, MM's total enjoyable value will be the sum of EiAi.

But actually, poor DD had only D dollars, and the price for the ith kind of cookies is Pi dollars per piece. DD must spend all his Ddollars to buy cookies, to meet requirements about amount and taste from MM, and to make MM's enjoyable value as high as possible. What's more, as you know, a legal plan's enjoyable value must be non-negative.

Input

There are multiple test cases. Each test case consists of three parts.

The first part is one line with two integers N and D.

The second part has N lines, line i consists of three integers KiEi and Pi. If Ki equals to 0, it means for ith kind of cookies, there didn't exist an upper bound that MM wanted to buy, otherwise Ki is the upper bound for ith kind of cookies.

The third part describes the groups. A non-negative integer G represents the number of groups, and then G lines, each line consists of some integers represents labels of kinds of cookies in this group.

One blank line between test cases.

Output

If the proper and optimal plan exists, output the maximal total enjoyable value ΣEiAi, otherwise output "i'm sorry...".

Output one line per text case.

Data Restriction

1 <=  N <= 1024, 0 <=  D <= 1024.

0 <=  Ki <= 1024, -1024 <=  Ei <= 1024, 0 <  Pi <=  D.

0 <=  G <= 8.

All numbers referred are integers.

Number of test cases is no more than 80.

Sample Input

2 1024
0 1 3
0 0 1
0

10 1023
1 1 1
1 1 2
1 1 4
1 1 8
1 1 16
1 1 32
1 1 64
1 1 128
3 -1 256
1 1 512
1
9 10

10 1023
1 1 1
1 1 2
1 1 4
1 1 8
1 1 16
1 1 32
1 1 64
1 1 128
1 1 256
1 1 512
1
9 10

Sample Output

341
5
i'm sorry...

Author:  CUI, Tianyi

Source: ZOJ Monthly, February 2009

分析:这题是背包九讲作者崔天翼出的题,这题算是挺复杂的背包题,里面用到多重背包,倍增思想,分组背包。。。
写得有点慢阿,懒得优化了

2012-9-26 昨晚看了09年xch的论文,对于背包的各种优化有了新的理解,然后就挑了这题来做了,用上了多重背包的队列优化,还有泛化物品的合并优化,也就是分组背包的一个优化吧,立马刷到了rank1,呵呵    代码在后面

代码:

#include<cstdio>
#include<iostream>
using namespace std;
const int mm=1111;
const int lm=-10000000;
int f[mm],tmp[mm],s[mm],e[mm],p[mm],t[mm],w[9][mm];
int i,j,k,v,l,r,n,d,g;
bool get(int g)
{
    int a,b;
    while(((b=getchar())<'0'||b>'9')&&b!='\n');
    if(b=='\n')return 0;
    for(a=0;b>='0'&&b<='9';b=getchar())a=a*10+b-'0';
    t[a-1]=g;
    return b!='\n';
}
void clear(int f[])
{
    for(int i=1;i<=d;++i)f[i]=-1000000000;
    f[0]=0;
}
void CompletePack(int v,int e,int f[])
{
    for(int i=v;i<=d;++i)f[i]=max(f[i],f[i-v]+e);
}
void ZeroOnePack(int v,int e,int f[])
{
    for(int i=d;i>=v;--i)f[i]=max(f[i],f[i-v]+e);
}
int main()
{
    while(scanf("%d%d",&n,&d)!=-1)
    {
        for(i=0;i<n;++i)scanf("%d%d%d",&s[i],&e[i],&p[i]),t[i]=0;
        scanf("%d",&g);
        for(getchar(),i=1;i<=g;++i)while(get(i));
        for(i=1;i<=g;++i)clear(w[i]);
        clear(f);
        for(i=0;i<n;++i)
            {
                if(t[i])clear(tmp);
                if(!s[i]||s[i]*p[i]>=d)CompletePack(p[i],e[i],t[i]?tmp:f);
                else
                {
                    j=1;
                    while(j<s[i])
                    {
                        ZeroOnePack(p[i]*j,e[i]*j,t[i]?tmp:f);
                        s[i]-=j;
                        j<<=1;
                    }
                    ZeroOnePack(p[i]*s[i],e[i]*s[i],t[i]?tmp:f);
                }
                if(t[i])for(j=1;j<=d;++j)w[t[i]][j]=max(w[t[i]][j],tmp[j]);
            }
        for(k=1;k<=g;++k)
            for(i=d;i>=0;--i)
                for(j=1;j<=i;++j)
                    if(f[i-j]>lm&&w[k][j]>lm)
                        f[i]=max(f[i],f[i-j]+w[k][j]);
        if(f[d]>=0)printf("%d\n",f[d]);
        else puts("i'm sorry...");
    }
    return 0;
}

代码2:

#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
const int mm=1111;
int f[mm],f1[mm],f2[mm],t[mm],e[mm],p[mm],s[mm],id[mm],q[mm],qw[mm];
int i,j,k,n,d,g;
bool get(int g)
{
    int a,b;
    while((b=getchar())<'0'||b>'9');
    for(a=0;b>='0'&&b<='9';b=getchar())a=a*10+b-'0';
    t[a-1]=g;
    return (b!='\n');
}
bool cmp(int a,int b)
{
    return t[a]<t[b];
}
void CompletePack(int e,int p)
{
    for(int i=0;i<=d;++i)f2[i]=f1[i];
    for(int i=p;i<=d;++i)
    {
        f2[i]=max(f2[i],f2[i-p]+e);
        f[i]=max(f[i],f2[i]);
    }
}
void MultiPack(int s,int e,int p)
{
    int i,j,k,l,r,now,m;
    for(i=0;i<p;++i)
        for(m=(d-i)/p,j=l=0,r=-1;j<=m;++j)
        {
            now=f1[k=j*p+i]-j*e;
            while(l<=r&&qw[r]<=now)--r;
            q[++r]=j,qw[r]=now;
            if(j-q[l]>s)++l;
            f[k]=max(f[k],qw[l]+j*e);
        }
}
int main()
{
    while(~scanf("%d%d",&n,&d))
    {
        for(i=0;i<n;++i)
            t[i]=0,id[i]=i,scanf("%d%d%d",&s[i],&e[i],&p[i]);
        scanf("%d",&g);
        for(i=1;i<=g;++i)while(get(i));
        sort(id,id+n,cmp);
        for(i=0;i<=d;++i)f1[i]=f[i]=-1e8;
        f1[0]=f[0]=0;
        for(i=0;i<n;++i)
        {
            k=id[i];
            if(!s[k]||s[k]*p[k]>=d)CompletePack(e[k],p[k]);
            else MultiPack(s[k],e[k],p[k]);
            if(!t[k]||t[k]!=t[id[i+1]])
                for(j=0;j<=d;++j)f1[j]=f[j];
        }
        if(f[d]>=0)printf("%d\n",f[d]);
        else puts("i'm sorry...");
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值