综合到变态的背包问题]ZOJ 3164 Cookie Choice

zoj 3164 cookie choice

原题链接 http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=3164


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 D dollars 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...


这题是我目前为止见过最综合的背包问题,没有之一。大致就是用d元钱买饼干,有1~n种选择,每种选择i有一个最多想买的数量k,价值e(可以为负数,0),和价格(代价)p,并且给出g个组,每组有一些饼干的编号,一个饼干最多在一个分组里面(还好这里限制了1个),同个组里面的饼干不能同时购买(要不组里所有饼干都不买,要不就只买其中一种)。问恰好用完d元钱,能不能做到,能的话能买到的最大价值是多少。


总之就是个背包问题,但有很多限制。

让dp[i][j]表示只考虑前i组和里面的饼干,花j元能得到的最大价值。这里要记得把没分进组里的饼干也添加成单独的一组。我第一遍的时候忘记了。


我觉得我的代码不是特别有效,但是至少还挺直接的想法


#include <iostream>
using namespace std;
int n,d,g;
int limit[1025];
int value[1025];
int price[1025];
int group[1025][1025];
int dp[1025][1025];
int main(int argc, const char * argv[]) {
    
    //数组初始化
    for (int i=0;i<1025;i++){
        for (int j=0;j<1025;j++){
            group[i][j]=0;
            dp[i][j]=-2000000;
        }
    }
    dp[0][0]=0;
    
    //输入
    cin >> n >> d;
    for (int i=1;i<=n;i++)
        cin >> limit[i] >> value[i] >> price[i];
    cin >>g;
    for (int i=1;i<=g;i++){
        int j =1;
        while (cin >> group[i][j]){
            if (cin.get() == '\n')
                break;
            j++;
        }
    }
    
    //如果没有分组的话,每个物品成一组
    if (g==0){
        for (int i =1;i<=n;i++)
            group[i][1]=i;
        g = n;
    }
    //如果有分组的话,没分进组里的元素一个一组加在group数组里面
    else{
        bool *ingroup = new bool[n];
        for (int i = 1;i<=n;i++) ingroup[i]=false;
        for (int i=1;i<=g;i++){
            for (int j=1;group[i][j]!= 0;j++){
                ingroup[group[i][j]] = true;
            }
        }
        int flag = g;
        for (int i=1;i<=n;i++){
            if (ingroup[i]==false){
                flag ++;
                group[flag][1]=i;
            }
        }
        g=flag;

    }
    //主循环
    for (int i=1;i<=g;i++){
        for (int j=d;j>=0;j--){
            int maximum = dp[i-1][j];
            for (int k=1;group[i][k]!= 0;k++){
                int item = group[i][k];
                int count = 1;
                int l;
                if (limit[item] == 0) l=1025;
                else l= limit[item];
                while ((count*price[item] <=j)&&(count <= l)){
                    int temp = count*value[item] + dp[i-1][j-count*price[item]];
                    maximum = max (maximum,temp);
                    count ++;
                }
                dp[i][j] = maximum;
            }
        }

    }
    
    if (dp[g][d] < 0)
        cout << "i am sorry..." << endl;
    else
        cout << dp[g][d] << endl;
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值