HDU 3033 I love sneakers!(分组背包/至少选一个)

31 篇文章 0 订阅
22 篇文章 0 订阅

题目链接:
HDU 3033 I love sneakers!
题意:
有n个物品分成k组,每个物品都有相应的价格和价值,每组物品都至少得买一件。有m元,如果能把k组每组至少买一件输出用m元能买到的最大价值;否自输出Impossible。
分析:
01背包的变形–分组背包,而且是每组至少买一个的分组背包而不是每组至多只能买一个。
用dp[i][k]表示在前i组每组至少选一样,耗费k能获得的最大价值,
初始化dp[i][k]为-1,但是dp[i][k]=0,这样是为了在购买第一组时可以从dp[0]状态中选择。
状态转移方程:

if(dp[i][k-cost[i][j]]!=-1){//在第i组至少已经选一件商品
    dp[i][k]=max(dp[i][k],dp[i][k-cost[i][j]]+val[i][j]);
}
if(dp[i-1][k-cost[i][j]]!=-1){//dp[i-1][k-cost[i][j]]!=-1保证前i-1组每组至少选一个
    dp[i][k]=max(dp[i][k],dp[i-1][k-cost[i][j]]+val[i][j]);
}

这两个if是独立的,也就是不能用else if相连,因为第一个if是判断是否在第i组中同时买第j件和之前在i组中买的;第二个if是判断是否在第i组中只买第j件

注意k的遍历顺序应该是从M–>cost[i][j]因为是滚动数组。

//109MS 2008K
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int MAX_N=110;
const int MAX_K=15;
const int MAX_M=10010;

int N,M,K;
int cost[MAX_K][MAX_N],val[MAX_K][MAX_N],num[MAX_K],dp[MAX_K][MAX_M];

int main()
{
    //freopen("Din.txt","r",stdin);
    while(~scanf("%d%d%d",&N,&M,&K)){
        memset(num,0,sizeof(num));
        for(int i=0;i<N;i++){
            int a,b,c;
            scanf("%d%d%d",&a,&b,&c);
            cost[a][num[a]]=b;
            val[a][num[a]]=c;
            num[a]++;
        }
        memset(dp,-1,sizeof(dp));
        for(int i=0;i<MAX_M;i++) dp[0][i]=0;
        for(int i=1;i<=K;i++){//dp[i][k]表示在前i组每组至少选一样,耗费k能获得的最大价值
            for(int j=0;j<num[i];j++){
                for(int k=M;k>=cost[i][j];k--){//注意k的遍历顺序!
                    if(dp[i][k-cost[i][j]]!=-1){//在第i组已经选一件商品
                        dp[i][k]=max(dp[i][k],dp[i][k-cost[i][j]]+val[i][j]);
                    }
                    if(dp[i-1][k-cost[i][j]]!=-1){//dp[i-1][k-cost[i][j]]!=-1保证前i-1组每组至少选一个
                        dp[i][k]=max(dp[i][k],dp[i-1][k-cost[i][j]]+val[i][j]);
                    }
                    //printf("i=%d j=%d k=%d dp[i][k]=%d\n",i,j,k,dp[i][k]);
                }
            }
        }
        if(dp[K][M]==-1) printf("Impossible\n");
        else printf("%d\n",dp[K][M]);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值