HDU 3535 AreYouBusy(混合背包+分组背包/至少取一件)

22 篇文章 0 订阅

题目链接:
HDU 3535 AreYouBusy
题意:
有n组物品,有三种组:①:组中物品必须买一个;②:组中物品最多买一个;③:组中物品随便买。每个物品只能买一次,问给m元钱能得到的最大价值是多少?如果无法得到最大价值,即有的组该买而无法买,那就输出-1.
分析:
混合背包和分组背包。
分两部分解决,一部分是分组背包,即每组中必须买一个;另一部分是至多买一个和随便买。然后最终只需要遍历一下两部分满足条件的所有能到的最大价值相加取最大就好了。
注意:
下面代码的中的

for(int j=0;j<=total;j++){ dp2[i][j]=dp2[i-1][j]; }

本来是用

dp2[i][k]=max(dp2[i][k],dp2[i-1][k]);

代替的,WA了好多发,如果是

dp2[i][k]=max(dp2[i][k],dp2[i-1][k]);

处理的话,那么对于

dp2[i][k]=max(dp2[i][k],dp2[i][k-task[i].cost[j]]+task[i].val[j]);

就有一个问题:在处理第一件物品的时候, dp2[i][ktask[i].cost[j]] 是0!而实际上应该是前面i-1组用 ktask[i].cost[j] 能得到的最大价值!o(╯□╰)o

//1604K 31MS
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int MAX_N=110;

int n,total;
int dp1[MAX_N][MAX_N],dp2[MAX_N][MAX_N];

struct Task{
    int num,flag;
    int cost[MAX_N],val[MAX_N];
    bool operator < (const Task a) const {
        if(flag!=a.flag) return flag<a.flag;
        else return num<a.num;
    }
}task[MAX_N];

int main()
{
    freopen("hdu3535in.txt","r",stdin);
    while(~scanf("%d%d",&n,&total)){
        int zero_num=0;
        for(int i=1;i<=n;i++){
            scanf("%d%d",&task[i].num,&task[i].flag);
            if(task[i].flag==0) zero_num++;
            for(int j=0;j<task[i].num;j++){
                scanf("%d%d",&task[i].cost[j],&task[i].val[j]);
            }
        }
        sort(task+1,task+n+1);

        memset(dp1,-1,sizeof(dp1));
        memset(dp1[0],0,sizeof(dp1[0]));
        for(int i=1;i<=zero_num;i++){
            for(int j=0;j<task[i].num;j++){
                for(int k=total;k>=task[i].cost[j];k--){
                    if(dp1[i][k-task[i].cost[j]]!=-1) {
                        dp1[i][k]=max(dp1[i][k],dp1[i][k-task[i].cost[j]]+task[i].val[j]);
                    }
                    if(dp1[i-1][k-task[i].cost[j]]!=-1){
                        dp1[i][k]=max(dp1[i][k],dp1[i-1][k-task[i].cost[j]]+task[i].val[j]);
                    }
                }
            }
        }
        if(dp1[zero_num][total]==-1){
            printf("-1\n");
            continue;
        }
        //printf("zero:%d\n",dp1[zero_num][total]);

        memset(dp2,0,sizeof(dp2));
        for(int i=zero_num+1;i<=n;i++){
            for(int j=0;j<=total;j++){
                dp2[i][j]=dp2[i-1][j];
            }
            for(int j=0;j<task[i].num;j++){
                for(int k=total;k>=task[i].cost[j];k--){
                    //dp2[i][k]=max(dp2[i][k],dp2[i-1][k]);
                    if(task[i].flag==1) //至多只能选一件
                        dp2[i][k]=max(dp2[i][k],dp2[i-1][k-task[i].cost[j]]+task[i].val[j]);
                    else {//不限制取件量
                        dp2[i][k]=max(dp2[i][k],dp2[i][k-task[i].cost[j]]+task[i].val[j]);
                    }
                    //printf("i=%d j=%d k=%d dp[i][k]=%d\n",i,j,k,dp2[i][k]);
                }
            }
        }

        int ans=0;
        for(int i=0;i<=total;i++){
            if(dp1[zero_num][i]!=-1){
                ans=max(ans,dp1[zero_num][i]+dp2[n][total-i]);
            }
        }
        printf("%d\n",ans);
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值