POJ - 1170 - Shopping Offers(状压dp)

Shopping Offers

Time Limit: 1000MS Memory Limit: 10000K
Total Submissions: 5333 Accepted: 2242
Description

In a shop each kind of product has a price. For example, the price of a flower is 2 ICU (Informatics Currency Units) and the price of a vase is 5 ICU. In order to attract more customers, the shop introduces some special offers.
A special offer consists of one or more product items for a reduced price. Examples: three flowers for 5 ICU instead of 6, or two vases together with one flower for 10 ICU instead of 12.
Write a program that calculates the price a customer has to pay for certain items, making optimal use of the special offers. That is, the price should be as low as possible. You are not allowed to add items, even if that would lower the price.
For the prices and offers given above, the (lowest) price for three flowers and two vases is 14 ICU: two vases and one flower for the reduced price of 10 ICU and two flowers for the regular price of 4 ICU.
Input

Your program is to read from standard input. The first line contains the number b of different kinds of products in the basket (0 <= b <= 5). Each of the next b lines contains three values c, k, and p. The value c is the (unique) product code (1 <= c <= 999). The value k indicates how many items of this product are in the basket (1 <= k <= 5). The value p is the regular price per item (1 <= p <= 999). Notice that all together at most 5*5=25 items can be in the basket. The b+2nd line contains the number s of special offers (0 <= s <= 99). Each of the next s lines describes one offer by giving its structure and its reduced price. The first number n on such a line is the number of different kinds of products that are part of the offer (1 <= n <= 5). The next n pairs of numbers (c,k) indicate that k items (1 <= k <= 5) with product code c (1 <= c <= 999) are involved in the offer. The last number p on the line stands for the reduced price (1 <= p <= 9999). The reduced price of an offer is less than the sum of the regular prices.
Output

Your program is to write to standard output. Output one line with the lowest possible price to be paid.
Sample Input

2
7 3 2
8 2 5
2
1 7 3 5
2 7 1 8 2 10
Sample Output

14
Source

IOI 1995

题目链接http://poj.org/problem?id=1170


题意

  • 多组输入
  • 每组先输入一个 b ,代表购物车里有 b 种物品。
  • 然后接下来输入的 b 行中,每行输入 c,k,p,代表购物车里编号为 c 的物品,有 k 件,每件的单价为 p
  • 接着输入一个 s,代表现在有 s 种优惠方案。
  • 接下来输入的 s 行中,每行先输入一个 n ,代表当前优惠方案中一共包含 n 种物品。
  • 然后输入 n 对(c,k),代表当前优惠方案购买 编号为 c 的物品 k 件。
  • 最后输入一个 p 代表 当前方案的花费。

:若要把购物车里的物品清空,最少要花多少钱?


解题思路

  • 先把物品离散化。
  • 因为购物车里最多只有 5 种物品(b <= 5),并且每种物品的数量也最多有 5 件(k <= 5)。所以最多一共有 6 * 6 * 6 * 6 * 6 种状态。可以把状态压缩为 6 进制,第 i 位上的值代表当前状态第 i 种物品的件数。
  • dp[i] : 达到 i 状态时,最少要花费多少钱。
  • 初始化:dp[0] = 0 ,其他初始化为正无穷
  • 转移方程:分为不使用优惠使用优惠的转移。具体见代码。
  • 最后答案:dp[sta] (sta = 最终状态)

代码如下

#include<cstdio>
#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<map>
using namespace std;

int dp[7800];
//这道题的状压DP,就是把5维数组压缩1个数组,也就是把它转换为6进制,每一位上的值(0 ~ 5)代表当前种类物品的个数。
//在写转移方程dp[i]时,首先要满足i在6进制下的每一位的值要大于当前优惠的相应物品的个数。
map<int,int> num;
int obj_sum[6],obj_p[6];
int n,s;

struct X{
    int sta;
    int p;
}item[105];

int cnt[10],all[10],k[10];

const int inf = 1 << 30;

void Init(){
    cnt[0] = 0;
    cnt[1] = 1;
    for(int i = 2;i <= 5;i++){
        cnt[i] = cnt[i - 1] * 6;
    }
}

bool check(int x,int y){
    int tail = 0;
    memset(k,0,sizeof(k));
    while(x != 0 || y != 0){
        if(x % 6 < y % 6) return false;
        k[++tail] = y % 6;
        x /= 6;
        y /= 6;
    }
    return true;
}

int main(){
    Init();
    while(~scanf("%d",&n)){
        int sta = 0;
        for(int i = 1;i <= n;i++){
            int x,y,z;
            scanf("%d %d %d",&x,&y,&z);
            num[x] = i;
            obj_sum[i] = y;
            obj_p[i] = z;
            sta += y * cnt[i];//最后要达到的状态。
        }
        scanf("%d",&s);
        for(int i = 1;i <= s;i++){
            int x,y,z;
            item[i].sta = 0;
            scanf("%d",&x);
            for(int j = 1;j <= x;j++){
                scanf("%d %d",&y,&z);
                item[i].sta += z * cnt[num[y]];//每个优惠的状态。
            }
            scanf("%d",&item[i].p);
        }
        for(int i = 1;i <= sta;i++) dp[i] = inf;
        dp[0] = 0;
        memset(all,0,sizeof(all));
        for(int i = 0;i <= sta;i++){
            int x,tail = 0;
            x = i;
            while(x){
                all[++tail] = x % 6;
                x /= 6;
            }
            dp[i] = 0;
            for(int j = 1;j <= 5;j++){//不用优惠,每种物品都按单价购买。
                dp[i] += all[j] * obj_p[j];
            }
            for(int j = 1;j <= s;j++){//用优惠
                if(i >= item[j].sta && check(i,item[j].sta)){//判断是否可以用当前的优惠。
                    int y = 0;
                    for(int l = 1;l <= 5;l++){
                        y += cnt[l] * k[l];
                    }
                    dp[i] = min(dp[i],dp[i - y] + item[j].p);
                }
            }
        }
        printf("%d\n",dp[sta]);
    }
}
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值