【状态压缩DP】POJ 1170

题意:一个商店提供多种商品,当用户单独购买商品时有一个价格,当用户组合购买时可以获得优惠,现在提供多种优惠方案和需要购买的物品总数,问最大的优惠是多少。
输入
2
7 3 2
8 2 5
2
1 7 3 5
2 7 1 8 2 10
表示有 2 种商品,编号分别是 7 和 8,分别要购买的数量是 3 和 2,2 和 5 是它们的单件购买价格。
接下来是 2 种优惠。第 1 种优惠有 1 种商品:3 个 7 号商品价格为 5,第 2 种优惠是 2 种商品:1 个 7 号商品和 2 个 8 号商品价格是 10。

最多只允许有 5 件商品。

用6进制表示所有状态。

#define N 55
int six[7] = {1,6,36,216,1296,7776};//因为最多只有5种物品而且每种物品最多只有5件,所以可以用6进制表示每种物品的状态
int index[1001];//吧编号都记录为1,2,3,4...
int dp[50000];
int n,m;
struct node{
    int id;
    int num,p;
}bask[10];
struct node1{
    int st;
    int p;
}offer[100];
int cal(int s){
    int i;
    int sum = 0;
    for(i=0;i<n;i++){
        sum += s%6*bask[i].p;
        s/=6;
    }
    return sum;
}
bool chk(int s1,int s2){//判断状态是否合法
    int i;
    for(i=0;i<n;i++){
        if(s1%6 + s2%6 > bask[i].num)return false;
        s1/=6,s2/=6;
    }
    return true;
}
int main(){
    while(scanf("%d",&n) != -1){
        int i,j;
        int state = 0;
        for(i=0;i<n;i++){
            scanf("%d%d%d",&bask[i].id,&bask[i].num,&bask[i].p);
            index[bask[i].id] = i;
            state += six[i]*bask[i].num;
        }
        scanf("%d",&m);
        for(i=0;i<m;i++){
            int k;
            scanf("%d",&k);
            offer[i].st = 0;
            while(k--){
                int id,num;
                scanf("%d%d",&id,&num);
                offer[i].st += num*six[index[id]];
            }
            scanf("%d",&offer[i].p);
        }
       for(i=0;i<=state;i++){
           dp[i] = MAX;
       }
       dp[0] = 0;
       for(i=0;i<m;i++){
           for(j=0;j+offer[i].st<=state;j++){
               if(dp[j]!=MAX && chk(j,offer[i].st)){
                   if(dp[j+offer[i].st] > dp[j] + offer[i].p){
                       dp[j+offer[i].st] = dp[j] + offer[i].p;//对可以打折的组合进行dp
                   }
               }
           }
       }
       int ans = MAX;
       for(i=0;i<=state;i++){
           int sum = cal(state-i);//计算单件买的
           ans = min(ans,dp[i]+sum);
       }
       printf("%d\n",ans);
    }
    return 0;
}














评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值