[HDU 3535] AreYouBusy 混合背包

http://acm.hdu.edu.cn/showproblem.php?pid=3535

题意:输入两个数 n, T 表示有 n 类工作和 T 个单位的时间,然后输入 n 类工作,每一类先输入两个数 m,s 表示这类工作包含 m 个工作, 如果 s == 0 表示这类工作里面至少选一个去做, s == 1 表示这类工作里面做多只能做一个, s == 2 表示这类工作你随便选几个, 然后输入 m 行的两个数表示工作需要的时间和能得到的价值。

思路:
第一类:至少选一项,对于这一组的dp的初值,应该全部赋为负无穷,这样才能保证不会出现都不选的情况。
状态转移方程为 dp[i][k] = max{ dp[i][k], dp[i-1][k-vol] +v al, dp[i][k-vol]+val }。
dp[i][k]是不选择当前工作;
dp[i-1][k-vol]+val是选择当前工作,但是是第一次在本组中选;
dp[i][k-cost[j]]+val[j]表示选择当前工作,并且不是第一次取。

第二类:最多选一项,
状态转移方程为dp[i][k]=max{ dp[i][k], dp[i-1][k-vol]+val}。
由于要保证得到全局最优解,所以在该组DP开始以前,应该将上一组的DP结果先复制到这一组的dp[i]数组里,因为这一组的数据是在上一组数据的基础上进行更新的。

第三类:任意选,
状态转移方程为dp[i][k]=max{ dp[i][k],dp[i-1][k-vol]+val,dp[i][k-vol]+val}。
同样要保证为得到全局最优解,先复制上一组解。

#include <cstdio>
#include <cstring>
#include <iostream>

using namespace std;

const int inf = (1<<31)-1;

int dp[105][105];

int main()
{
    int n, volume;
    while(cin>>n>>volume){
        int m, s, val, vol;
        memset(dp, 0, sizeof(dp));
        for(int i = 1; i <= n; i++){
            cin>>m>>s;
            if(s == 0){
                for(int k = 0; k <= volume; k++){
                    dp[i][k] = -inf;
                }
                while(m--){
                    cin>>vol>>val;
                    for(int k = volume; k >= vol; k--){
                        dp[i][k] = max(dp[i][k], dp[i][k-vol] + val);//不是第一次选
                        dp[i][k] = max(dp[i][k], dp[i-1][k-vol] + val);//第一次选必须由上一层传递
                    }
                }
            }
            else if(s == 1){
                for(int k = 0; k <= volume; k++){
                    dp[i][k] = dp[i-1][k];
                }
                while(m--){
                    cin>>vol>>val;
                    for(int k = volume; k >= vol; k--){
                        dp[i][k] = max(dp[i][k], dp[i-1][k-vol] + val); //必须由上一次传递,保证之选一个
                    }
                }
            }
            else{
                for(int k = 0; k <= volume; k++){
                    dp[i][k] = dp[i-1][k];
                }
                while(m--){
                    cin>>vol>>val;
                    for(int k = volume; k >= vol; k--){
                        dp[i][k] = max(dp[i][k], dp[i][k-vol] + val); //01背包
                        dp[i][k] = max(dp[i][k], dp[i-1][k-vol] + val);
                    }
                }
            }
        }
        cout<<max(-1, dp[n][volume])<<endl;
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

achonor

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值