编程之美(四)买书问题

版权声明:本文为博主原创文章,转载请注明出处-- https://blog.csdn.net/qq_38790716/article/details/90321525

题目:

在节假日的时候,书店一般都会做促销活动。由于《哈利波特》系列相当畅销,店长决定通过促销活动来回馈读者。上柜的《哈利波特》平装书系列中,一共有五卷。假设每一卷单独销售均需 8 8 欧元。如果读者一次购买不同的两卷,就可以扣除 5 5 %的费用,三卷则更多,假设具体折扣的情况如下:

本数 折扣
2 5%
3 10%
4 20%
5 25%

在一份订单中,根据购买的卷数及本数,就会出现可以应用不同折扣规则的情况。但是,一本书只会应用一个折扣规则。比如,读者一共买了两本卷一,一本卷二。那么,可以享受 5 5 %的折扣。另外一本卷一则不能享受折扣。如果有多种折扣,希望计算出的总额尽可能的低。

要求根据以上需求,设计出算法,能够计算出读者所购买一批书的最低价格。

1. 贪心

对于这样的一个问题,我们很容易的带入我们的主观想法,即认为先考虑最大的折扣,然后次之,采取的策略能最省钱。那么我们按照这样的策略进行一个简单的分析,得到下列的一个折扣计算表:

本数 可能的分解组合 对应的折扣
对于 2 5 2-5 本书(不同卷),直接按折扣买 2 / 3 / 4 / 5 {2 /3 /4/ 5} 0.1 / 0.3 / 0.8 / 1.25 0.1/ 0.3/0.8/ 1.25
6 6 = 5 + 1 / = 4 + 2 / = 3 + 3 / = 2 + 2 + 2 = 5 + 1/ = 4 + 2/ = 3 + 3/ =2 + 2 + 2 1.25 / 0.9 / 0.6 / 0.3 1.25/ 0.9 /0.6 / 0.3
7 7 = 5 + 2 / = 4 + 3 / = 3 + 2 + 2 =5 + 2/ = 4 + 3/ = 3 + 2 + 2 1.35 / 1.1 / 0.5 1.35 / 1.1/ 0.5
8 8 = 5 + 3 / 4 + 4 / 3 + 3 + 2 / 2 + 2 + 2 + 2 =5 + 3/ 4 + 4/ 3 + 3 + 2/ 2 + 2 + 2 + 2 1.55 / 1.6 / 0.7 / 0.4 1.55 / 1.6/ 0.7/ 0.4
9 9 = 5 + 4 / = 5 + 2 + 2 / = 4 + 3 + 2 / = 3 + 3 + 3 = 5 + 4/ = 5 + 2 + 2/ = 4 + 3 + 2/ = 3 + 3 + 3 2.05 / 1.45 / 1.2 / 0.9 2.05/ 1.45/ 1.2/ 0.9
10 10 = 5 + 5 / = 4 + 4 + 2 / 4 + 3 + 3 / = 2 + 2 + 2 + 2 + 2 / = 5 + 5/ = 4 + 4 + 2/ 4 + 3 + 3/ = 2 + 2 +2 + 2 + 2/ 2.5 / 1.7 / 1.4 / 0.5 2.5/ 1.7/ 1.4/ 0.5

对于目前分析到的总数为 10 10 本以下的情况,可以看到当总数为 8 8 时,分为 5 + 3 5 + 3 的情况所得折扣小于分为 4 + 4 4 + 4 所得的折扣,仅这一条足以推翻该贪心策略。

书中虽然尝试着优化贪心,但始终未能找到合适的方式,于是给出了动态规划的想法

2. 动态规划
  • d p [ y 1 ] [ y 2 ] [ y 3 ] [ y 4 ] [ y 5 ] dp[y1][y2][y3][y4][y5] 表示购买 y 1 y1 本卷一、 y 2 y2 本卷二、 y 3 y3 本卷三、 y 4 y4 本卷四及y5本卷五所需最少花费
  • y 1 = y 2 = y 3 = y 4 = y 5 = 0 y1 = y2 = y3 = y4 = y5 = 0 时,也就是说购买 0 0 本书,自然所花费为 0 0
  • 首先保证 y 1 > = y 2 > = y 3 > = y 4 > = y 5 y1 >= y2 >= y3 >= y4 >= y5 (减少相同的情况) (1)当 y 5 > = 1 y5 >= 1 时,考虑选择 5 5 本书的最大折扣 + 剩余书的最少花费,即 d p [ y 1 ] [ y 2 ] [ y 3 ] [ y 4 ] [ y 5 ] dp[y1][y2][y3][y4][y5] = 5 8 ( 1 25 5 * 8 * (1 - 25 %) + d p [ y 1 1 ] [ y 2 1 ] [ y 3 1 ] [ y 4 1 ] [ y 5 1 ] dp[y1 - 1][y2 - 1][y3 - 1][y4 - 1][y5 - 1] (2)当 y 4 > = 1 y4 >= 1 时,考虑选择 4 4 本书的最大折扣 + 剩余书的最少花费,即 d p [ y 1 ] [ y 2 ] [ y 3 ] [ y 4 ] [ y 5 ] dp[y1][y2][y3][y4][y5] = 4 8 ( 1 20 4 * 8 * (1 - 20 %) + d p [ y 1 1 ] [ y 2 1 ] [ y 3 1 ] [ y 4 1 ] [ y 5 ] dp[y1 - 1][y2 - 1][y3 - 1][y4 - 1][y5] …其余几种选项同理,那么对于当前的 y 1 , y 2 , y 3 , y 4 , y 5 y1,y2,y3,y4,y5 来说,既然有这几种可能,就选择其中最少的花费作为它们的最少花费
//状态转移方程
dp[y1][y2][y3][y4][y5] 
= 0                            if (y1 = y2 = y3 = y4 = y5 = 0)
= min {
	5 * 8 * (1 - 25%) + dp[y1 - 1][y2 - 1][y3 -1][y4 - 1][y5 - 1],        if(y5 >= 1)
	4 * 8 * (1 - 20%) + dp[y1 - 1][y2 - 1][y3 - 1][y4 - 1][y5],             if (y4 >= 1)
	3 * 8 * (1 - 10%) + dp[y1 - 1][y2 - 1][y3 - 1][y4][y5],                  if (y3 >= 1)
	2 * 8 * (1 - 5 %) + dp[y1 - 1][y2 - 1][y3][y4][y5],                 if (y2 >= 1)
	8 + dp[y1 - 1][y2][y3][y4][y5],   if (y1 >= 1)
}
3. 实现代码

非递归实现动态规划:

#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
#define price 8
#define INF 9999
int book[5];
double discount[4] = {0.75, 0.8, 0.9, 0.95};
double dp[10][10][10][10][10];
double min_price(double t1, double t2, double t3, double t4, double t5) {
    return min(min(min(min(t1, t2), t3), t4), t5);
}
int main() {
    for (int i = 0; i < 5; ++i) {
        cin >> book[i];
    }
    memset(dp, 0, sizeof(dp));
    sort(book, book + 5, greater<int>());
    for (int i = 0; i <= book[4]; ++i) {
        for (int j = i; j <= book[3]; ++j) {
            for (int k = j; k <= book[2]; ++k) {
                for (int l = k; l <= book[1]; ++l) {
                    for (int m = l; m <= book[0]; ++m) {
                    	//初始条件,这一点卡了很长时间
                        if (i + j + k + m + l == 0)
                            continue;
                        double t1 = INF, t2 = INF, t3 = INF, t4 = INF, t5 = INF;
                        if (i >= 1) {
                            int num[5] = {m - 1, l - 1, k - 1, j - 1, i - 1};
                            sort(num, num + 5, greater<int>());
                            t1 = 5 * price * discount[0] + dp[num[0]][num[1]][num[2]][num[3]][num[4]];
                        }
                        if (j >= 1) {
                            int num[5] = {m - 1, l - 1, k - 1, j - 1, i};
                            sort(num, num + 5, greater<int>());
                            t2 = 4 * price * discount[1] + dp[num[0]][num[1]][num[2]][num[3]][num[4]];
                        }
                        if (k >= 1) {
                            int num[5] = {m - 1, l - 1, k - 1, j, i};
                            sort(num, num + 5, greater<int>());
                            t3 = 3 * price * discount[2] + dp[num[0]][num[1]][num[2]][num[3]][num[4]];
                        }
                        if (l >= 1) {
                            int num[5] = {m - 1, l - 1, k, j, i};
                            sort(num, num + 5, greater<int>());
                            t4 = 2 * price * discount[3] + dp[num[0]][num[1]][num[2]][num[3]][num[4]];
                        }
                        if (m >= 1) {
                            int num[5] = {m - 1, l, k, j, i};
                            sort(num, num + 5, greater<int>());
                            t5 = price + dp[num[0]][num[1]][num[2]][num[3]][num[4]];
                        }
                        dp[m][l][k][j][i] = min_price(t1, t2, t3, t4, t5);
                    }
                }
            }
        }
    }
    cout << dp[book[0]][book[1]][book[2]][book[3]][book[4]] << endl;
    return 0;
}

递归实现动态规划:

#include <iostream>
#include <algorithm>
using namespace std;
#define price 8
#define INF 9999
int book[5];
double discount[4] = {0.75, 0.8, 0.9, 0.95};
double min_price(double t1, double t2, double t3, double t4, double t5) {
    return min(min(min(min(t1, t2), t3), t4), t5);
}
double F(int y1, int y2, int y3, int y4, int y5) {
	if (y1 + y2 + y3 + y4 + y5 == 0)
		return 0;

	int num[5] = {y1, y2, y3, y4, y5};
	sort(num, num + 5, greater<int>());

	double t1 = INF, t2 = INF, t3 = INF, t4 = INF, t5 = INF;
	if (num[4] >= 1) t1 = 5 * price * discount[0] + F(num[0] - 1, num[1] - 1, num[2] - 1, num[3] - 1, num[4] - 1);
	if (num[3] >= 1) t2 = 4 * price * discount[1] + F(num[0] - 1, num[1] - 1, num[2] - 1, num[3] - 1, num[4]);
	if (num[2] >= 1) t3 = 3 * price * discount[2] + F(num[0] - 1, num[1] - 1, num[2] - 1, num[3], num[4]);
	if (num[1] >= 1) t4 = 2 * price * discount[3] + F(num[0] - 1, num[1] - 1, num[2], num[3], num[4]);
	if (num[0] >= 1) t5 = price + F(num[0] - 1, num[1], num[2], num[3], num[4]);

	return min_price(t1, t2, t3, t4, t5);
}
int main() {
	for (int i = 0; i < 5; ++i) {
		cin >> book[i];
	}
	sort(book, book + 5, greater<int>());
	cout << F(book[0], book[1], book[2], book[3], book[4]) << endl;
	return 0;
}

测试样例:

//1
{1, 0, 0, 0, 0}

在这里插入图片描述

//2
{2, 2, 2, 1, 1}

在这里插入图片描述

扫描二维码关注公众号,回复: 6743770 查看本文章

猜你喜欢

转载自blog.csdn.net/qq_38790716/article/details/90321525
今日推荐