HDU 3236 Gift Hunting (01背包变型)

101 篇文章 1 订阅

Gift Hunting

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



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: P, H 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
  大体题意:
你有两张优惠券,要去一个大型购物商场去买东西,不同的东西会让你的女朋友幸福指数不同,特殊的东西必须全部买到,普通的东西能买就买,如果特殊的东西不能全部买到,则输出-1,并且你还有一个免费拿一个东西的机会,求出最大幸福指数是多少,注意,两张优惠券不能合在一起!
思路:
01背包变型版,比赛时没有写出来= =!
其实不论是特殊的东西,还是普通的东西,都得用dp转移,当时没有注意这一点!
令DP[i][j][s]  表示第一张优惠券剩余i元时,第二张优惠券剩余j元时,s状态的幸福指数,其中s = 0 表示没有用到免费拿东西的机会,s = 1,表示已经用了免费拿一个东西的机会!
先处理特殊的东西:
枚举每一个东西时,有一个当前总和和前一个总和,当dp[j][k][0]和dp[j][k][1]  等于前一个总和时,才有机会全部拿到!
if (dp[j][k][0] == pre){
    if (j + p2[i].p <= v1) dp[j + p2[i].p ][k][0] = max(dp[j][k][0] + p2[i].h,dp[j + p2[i].p ][k][0]);
    if (k + p2[i].p <= v2) dp[j][k + p2[i].p][0] = max(dp[j][k][0] + p2[i].h,dp[j][k + p2[i].p][0]);
     dp[j][k][1] = sum;
}
if (dp[j][k][1] == pre){
    if (j + p2[i].p <= v1) dp[j + p2[i].p ][k][1] = max(dp[j][k][1] + p2[i].h,dp[j + p2[i].p ][k][1]);
    if (k + p2[i].p <= v2) dp[j][k + p2[i].p][1] = max(dp[j][k][1] + p2[i].h,dp[j][k + p2[i].p][1]);
}

对于普通的东西,就是普通的01背包了!
也就是说只有在i,j,k能获得所有特殊东西时,才转移!
最后选取一个最大的dp[i][j][k]即可!
详细见代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<cstdlib>
#include<string>
#include<vector>
#include<set>
#include<map>
#include<cctype>
#include<iostream>
using namespace std;
const int maxn = 300 + 10;
const int inf = 0x3f3f3f3f;
const double eps = 1e-8;
const double pi = acos(-1.0);
struct Node1{
    int p,h;
    bool operator < (const Node1 & rhs) const {
        return h > rhs.h || (h == rhs.h && p < rhs.p);
    }
}p1[maxn];
struct Node2{
    int p,h;
    bool operator < (const Node2 & rhs) const {
        return h > rhs.h || (h == rhs.h && p < rhs.p);
    }
}p2[maxn];
double sum[maxn];
int dp[507][507][2];
int vis[maxn];
int main(){
    int v1,v2,n,kase = 0;
    while(scanf("%d %d %d",&v1,&v2,&n) == 3 && (v1 || v2 || n)){
        int spe = 0;
        int com = 0;
        memset(dp,-1,sizeof dp);
        memset(vis,0,sizeof vis);
        for (int i = 0; i < n; ++i){
            int pr,h,s;
            scanf("%d %d %d",&pr,&h,&s);
            if (s == 0){
                p1[com].p = pr;
                p1[com++].h = h;
            }
            else {
                p2[spe].p = pr;
                p2[spe++].h = h;
            }
        }


        dp[0][0][0] = 0;

        // ==========================================
        // to get special gift!!!
        int sum = 0,pre = 0;
        for (int i = 0 ; i < spe; ++i){
            pre = sum;
            sum += p2[i].h;
            for (int j = v1; j >= 0; --j){
                for (int k = v2; k >= 0; --k){
                    if (dp[j][k][0] == pre){
                        if (j + p2[i].p <= v1) dp[j + p2[i].p ][k][0] = max(dp[j][k][0] + p2[i].h,dp[j + p2[i].p ][k][0]);
                        if (k + p2[i].p <= v2) dp[j][k + p2[i].p][0] = max(dp[j][k][0] + p2[i].h,dp[j][k + p2[i].p][0]);
                        dp[j][k][1] = sum;
                    }
                    if (dp[j][k][1] == pre){
                        if (j + p2[i].p <= v1) dp[j + p2[i].p ][k][1] = max(dp[j][k][1] + p2[i].h,dp[j + p2[i].p ][k][1]);
                        if (k + p2[i].p <= v2) dp[j][k + p2[i].p][1] = max(dp[j][k][1] + p2[i].h,dp[j][k + p2[i].p][1]);
                    }
                }
            }
        }
        // ============================================

        bool ok = false;
        for (int i = v1; i >= 0; --i){
            for (int j = v2; j >= 0; --j){
                for (int k = 0; k < 2; ++k){
                    if (dp[i][j][k] == sum){
                        ok = true;
                    }else dp[i][j][k] = -1;
                }
            }
        }
        if (!ok){
            printf("Case %d: -1\n\n",++kase);
            continue;

        }
        // =============================================

        // to get common gift!!!
        for (int i = 0; i < com; ++i){
            for (int j = v1; j >= 0; --j){
                for (int k = v2; k >= 0; --k){
                    for (int s = 0; s < 2; ++s){
                        if (dp[j][k][s] != -1){
                            if (j + p1[i].p <= v1) dp[j + p1[i].p][k][s] = max(dp[j + p1[i].p][k][s],dp[j][k][s] + p1[i].h);
                            if (k + p1[i].p <= v2) dp[j][k + p1[i].p][s] = max(dp[j][k + p1[i].p][s],dp[j][k][s] + p1[i].h);
                        }
                    }
                    if (dp[j][k][0] != -1){
                        dp[j][k][1] = max(dp[j][k][1],dp[j][k][0] + p1[i].h);
                    }
                }

            }
        }
        // =============================================

         // to get answer!
         int ans = -1;
        for (int i = v1; i >= 0; --i){
            for (int j = v2; j >= 0; --j){
                for (int k = 0; k < 2; ++k){
                    ans = max(ans,dp[i][j][k]);
                }
            }

        }


        // =============================================
        printf("Case %d: %d\n\n",++kase,ans);
    }
    return 0;
}




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值