最小费用购物问题


西安交大 软件53 蔡少斐

题号:3_14

题目叙述:

某商店中每种商品都有一个价格。例如,一朵花的价格是2 ICU(ICU 是信息学竞赛的货币的单位);一个花瓶的价格是5 ICU。为了吸引更多的顾客,商店提供了特殊优惠价。

    特殊优惠商品是把一种或几种商品分成一组。并降价销售。例如:3朵花的价格不是6而是5 ICU ;2个花瓶加1朵花是10 ICU不是12 ICU。

    编一个程序,计算某个顾客所购商品应付的费用。要充分利用优惠价以使顾客付款最小。请注意,你不能变更顾客所购商品的种类及数量,即使增加某些商品会使付款总数减小也不允许你作出任何变更。假定各种商品价格用优惠价如上所述,并且某顾客购买物品为:3朵花和2个花瓶。那么顾客应付款为14ICU因为:

        1朵花加2个花瓶: 优惠价:10  ICU

        2朵花           正常价: 4 ICU

输入数据

    用两个文件表示输入数据。第一个文件INPUT.TXT描述顾客所购物品(放在购物筐中);第二个文件描述商店提供的优惠商品及价格(文件名为OFFER.TXT)。 两个文件中都只用整数。

    第一个文件INPUT.TXT的格式为:第一行是一个数字B(0≤B≤5),表示所购商品种类数。下面共B行,每行中含3个数C,K,P。C 代表商品的编码(每种商品有一个唯一的编码),1≤C≤999。K代表该种商品购买总数,1≤K≤5。P 是该种商品的正常单价(每件商品的价格),1≤P≤999。请注意,购物筐中最多可放5*5=25件商品。

第二个文件OFFER.TXT的格式为:第一行是一个数字S(0≤S≤99),表示共有S种优惠。下面共S行,每一行描述一种优惠商品的组合中商品的种类。下面接着是几个数字对(C,K),其中C代表商品编码,1≤C≤9 99。K代表该种商品在此组合中的数量,1≤K≤5。本行最后一个数字P(1≤P≤9999)代表此商品组合的优惠价。当然,优惠价要低于该组合中商品正常价之总和。

输出数据

在输出文件OUTPUT.TXT中写一个数字(占一行), 该数字表示顾客所购商品(输入文件指明所购商品)应付的最低货款。

题目解答:

本题应该采用动态规划的方法进行设计,定义本题的最优子结构以及状态为一个五元组:dp[x1][x2][x3][x4][x5],其中x1代表要买的第一种物品的个数,x2代表要买的第二种物品的个数、以此类推。由于题目保证了B<=5,因此5元组绝对够用。

我们用一个std::vector来存储每套组合方案的捆绑的种类以及该种类需要购买的数量。

下面我们假定,不需要的物品一个都不能买,需要的物品也不能够多买。

要列出该5元组的状态转移方程,其中优惠集合记为S。

.

在代码实现的时候采用了备忘录技术。

代码表示:

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
typedef pair<int, int> P;
const int MAX       = 6;
const int INF  = 1e9;
int          map[1000];
int          n, m;
int          ids[MAX];
int          price[MAX];
int          nums[MAX];
vector<P>     pairs[100];
int          pP[100];
int          pcnt = 0;
int          dp[MAX][MAX][MAX][MAX][MAX];
int          times = 0;
int dfs( int* x )
{
       times++;
       int r = dp[x[0]][x[1]][x[2]][x[3]][x[4]];
       if ( r > 0 )
       {
              return(r);
       }
       if ( x[0] == 0 && x[1] == 0 && x[2] == 0 && x[3] == 0 && x[4] == 0 )
       {
              return(0);
       }
       int minf = INF;
       for ( int i = 0; i < pcnt; i++ )
       {
              vector<P> & vec  = pairs[i];
              int          f      = 1;
              int          *y    = new int[5];
              for ( int t = 0; t < 5; t++ )
                     y[t] = 0;
              for ( auto p : vec )
              {
                     int   id    = map[p.first];
                     int   num = p.second;
                     if ( x[id] < num )
                     {
                            f = 0; break;
                     }
                     y[id] = -num;
              }
              if ( !f )
                     continue;
              for ( int k = 0; k < 5; k++ )
                     y[k] += x[k];
              minf = min( minf, pP[i] + dfs( y ) );
       }
       int s = 0;
       for ( int i = 0; i < 5; i++ )
       {
              s += x[i] * price[i];
       }
       minf = min( minf, s );
       return(dp[x[0]][x[1]][x[2]][x[3]][x[4]] = minf);
}
 
 
int main()
{
       cin >> n;
       for ( int i = 0; i < n; i++ )
       {
              int C, K, PP;
              cin >> C >> K >> PP;
              ids[i]              = C;
              nums[i]          = K;
              price[i]    = PP;
              if ( !map[C] )
              {
                     map[C] = i;
              }
       }
       cin >> m;
       for ( int i = 0; i < m; i++ )
       {
              int          k; cin >> k;
              vector<P>     v;
              int          f = 1;
              for ( int j = 0; j < k; j++ )
              {
                     int a, b; cin >> a >> b;
                     v.push_back( make_pair( a, b ) );
              }
              int PP;
              cin >> PP;
              if ( f )
              {
                     pairs[pcnt]     = v;
                     pP[pcnt++]    = PP;
              }
       }
       cout << "答案:" << dfs( nums ) << endl;
       cout << "运行次数:" << times << endl;
       return(0);
}

运行结果:


  • 1
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
最少购物费用动态规划是一种经典的动态规划问题,其目标是在购买商品时,使得总费用最小。该问题满足无后效性和最优化原理,因此可以使用动态规划来解决。 具体来说,我们可以使用一个五元组来表示第i种商品买ai的最小费用,即f(i,ai),其中i表示商品的种类,ai表示购买该商品的数量。那么,对于第i种商品,其购买数量ai的范围为0到5,因此我们可以使用一个二重循环来枚举所有的状态,即: ```python for i in range(1, n+1): for j in range(0, 6): # 计算f(i,j) ``` 对于每个状态f(i,ai),我们需要考虑两种情况:购买第i种商品和不购买第i种商品。如果购买第i种商品,那么总费用为f(i-1,aj)+pi*ai,其中aj表示购买第i-1种商品的数量,pi表示第i种商品的价格。如果不购买第i种商品,那么总费用为f(i-1,aj),即购买前i-1种商品的最小费用。因此,我们可以得到状态转移方程: f(i,ai) = min{f(i-1,aj)+pi*ai, f(i-1,aj)} 最终的答案为f(n, m),其中n表示商品的种类,m表示购买商品的总数量。 下面是一个Python实现的例子: ```python n = 5 # 商品种类 m = 5 # 购买商品的总数量 p = [0, 2, 3, 4, 5, 6] # 商品价格 a = [[0] * 6 for _ in range(6)] # a[i][j]表示第i种商品购买j个的最小费用 # 初始化 for i in range(1, n+1): for j in range(0, 6): if j == 0: a[i][j] = 0 else: a[i][j] = float('inf') # 动态规划 for i in range(1, n+1): for j in range(1, m+1): for k in range(0, j+1): a[i][j] = min(a[i][j], a[i-1][j-k]+p[i]*k) print(a[n][m]) # 输出最小费用 ```

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值