hdu 3236 Gift Hunting 二维01背包

Gift Hunting

Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 1261    Accepted Submission(s): 400


Problem Description
After winning two coupons for the largest shopping mart in your city, you can't wait inviting your girlfriend for gift hunting. Having inspected hundreds of kinds of souvenirs, toys and cosmetics, you finally narrowed down the candidate list to only  n gifts, numbered 1 to  n. Each gift has a happiness value that measures how happy your girlfriend would be, if you get this gift for her. Some of them are special - you  must get it for your girlfriend (note that whether a gift is special has nothing to do with its happiness value).

Coupon 1 can be used to buy gifts with total price not greater than  V1 (RMB). Like most other coupons, you  can’t get any money back if the total price is strictly smaller than  V1. Coupon 2 is almost the same, except that it’s worth  V2. Coupons should be used separately. That means you cannot combine them into a super-coupon that’s worth  V1+ V2. You have to divide the gifts you choose into two part, one uses coupon 1, the other uses coupon 2.

It is your girlfriend's birthday today. According to the rules of the mart, she can take one (only one) gift for FREE! Here comes your challenge: how to make your girlfriend as happy as possible?
 

Input
There will be at most 20 test cases. Each case begins with 3 integers  V1,  V2 and  n (1 <=  V1 <= 500, 1 <=  V2 <= 50, 1 <=  n <= 300), the values of coupon 1 and coupon 2 respectively, and the number of candidate gifts. Each of the following  n lines describes a gift with 3 integers:  PH and  S, where  P is the price,  H is the happiness (1 <=  P,H <= 1000),  S=1 if and only if this is a special gift - you must buy it (or get it for free). Otherwise  S=0. The last test case is followed by  V1 =  V2 =  n = 0, which should not be processed.
 

Output
For each test case, print the case number and the maximal total happiness of your girlfriend. If you can't finish the task, i.e. you are not able to buy all special gifts even with the 1-FREE bonus, the happiness is -1 (negative happiness means she's unhappy). Print a blank line after the output of each test case.
 

Sample Input
  
  
3 2 4 3 10 1 2 10 0 5 100 0 5 80 0 3 2 4 3 10 1 2 10 0 5 100 0 5 80 1 0 0 0
 

Sample Output
  
  
Case 1: 120 Case 2: 100
 

Source
 

Recommend
chenrui   |   We have carefully selected several similar problems for you:   3231  3233  3234  3237  3232 
 


这道题的2个复杂点:

    1、必须要获得全部的特殊礼物

    2、可以免费获得一件礼物

做训练赛的时候遇见的这道题,当时考虑的是假设有m件特殊礼物,那最终一定只会有2种情况,一种是获得全部的特殊礼物,在非特殊礼物中选一件价值最大的作为赠送礼物。另一种情况是买到m-1件特殊礼物,剩下的一个特殊礼物作为赠送礼物,如果第二种情况无法达到,就输出-1.  开的是dp[V1][V2][2]的数组。dp[i][j][0]表示背包状态为i,j时所有特殊物品全部购买的最大总价值。dp[i][j][1]表示有一件特殊礼物没有购买的最大总价值,同时还要另开一个数组记录路径的赠送价值。

其实思路是对的,状态转移方程也可以写出来。但是比赛的时候没有敲出来,之后自己想了想,状态转移复杂,没有事先写好就先敲代码,很容易写错,以后要杜绝这种情况,开三维的数组也很容易出错,也因为自己写代码的时候有的情况考虑不到。

之后看了别人的思路是0表示没用赠送机会,1表示用了,其实感觉大同小异。这道题用三维四维都可以做,四维的好想一点,避免出现意外。开了四维的滚动数组优化了下空间,不然会mle,等有时间用自己的思路做一遍试试。

#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;
const int N=305;
const int inf=0x3f3f3f3f;
int dp[2][505][55][2],p[N],v[N],g[N],cas=1;
int v1,v2,n;
int main()
{
    while(scanf("%d%d%d",&v1,&v2,&n),v1+v2+n){
        for(int i=1;i<=n;i++)
            scanf("%d%d%d",&p[i],&v[i],&g[i]);
        memset(dp,-inf,sizeof(dp));
        dp[0][v1][v2][0]=0;
        for(int i=1;i<=n;i++){
            for(int j=v1;j>=0;j--){
                for(int k=v2;k>=0;k--){
                    for(int t=0;t<2;t++){
                        if(dp[1-i%2][j][k][t]>=0){
                            if(g[i]==0)dp[i%2][j][k][t]=max(dp[i%2][j][k][t],dp[1-i%2][j][k][t]);
                            if(j>=p[i])dp[i%2][j-p[i]][k][t]=max(dp[i%2][j-p[i]][k][t],dp[1-i%2][j][k][t]+v[i]);
                            if(k>=p[i])dp[i%2][j][k-p[i]][t]=max(dp[i%2][j][k-p[i]][t],dp[1-i%2][j][k][t]+v[i]);
                            if(t==0)dp[i%2][j][k][1]=max(dp[i%2][j][k][1],dp[1-i%2][j][k][0]+v[i]);
                        }
                    }
                }
            }
        memset(dp[1-i%2],-inf,sizeof(dp[1-i%2]));
        }
        int ans=-1;
        for(int i=0;i<=v1;i++){
            for(int j=0;j<=v2;j++){
                ans=max(dp[n%2][i][j][1],ans);
            }
        }
    printf("Case %d: %d\n\n",cas++,ans);
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值