版权声明:本文为博主原创文章,转载请注明出处-- https://blog.csdn.net/qq_38790716/article/details/90321525
题目:
在节假日的时候,书店一般都会做促销活动。由于《哈利波特》系列相当畅销,店长决定通过促销活动来回馈读者。上柜的《哈利波特》平装书系列中,一共有五卷。假设每一卷单独销售均需 欧元。如果读者一次购买不同的两卷,就可以扣除 %的费用,三卷则更多,假设具体折扣的情况如下:
本数 | 折扣 |
---|---|
2 | 5% |
3 | 10% |
4 | 20% |
5 | 25% |
在一份订单中,根据购买的卷数及本数,就会出现可以应用不同折扣规则的情况。但是,一本书只会应用一个折扣规则。比如,读者一共买了两本卷一,一本卷二。那么,可以享受 %的折扣。另外一本卷一则不能享受折扣。如果有多种折扣,希望计算出的总额尽可能的低。
要求根据以上需求,设计出算法,能够计算出读者所购买一批书的最低价格。
1. 贪心
对于这样的一个问题,我们很容易的带入我们的主观想法,即认为先考虑最大的折扣,然后次之,采取的策略能最省钱。那么我们按照这样的策略进行一个简单的分析,得到下列的一个折扣计算表:
本数 | 可能的分解组合 | 对应的折扣 |
---|---|---|
对于 本书(不同卷),直接按折扣买 | ||
对于目前分析到的总数为 本以下的情况,可以看到当总数为 时,分为 的情况所得折扣小于分为 所得的折扣,仅这一条足以推翻该贪心策略。
书中虽然尝试着优化贪心,但始终未能找到合适的方式,于是给出了动态规划的想法
2. 动态规划
- 以 表示购买 本卷一、 本卷二、 本卷三、 本卷四及y5本卷五所需最少花费
- 当 时,也就是说购买 本书,自然所花费为 ,
- 首先保证 (减少相同的情况) (1)当 时,考虑选择 本书的最大折扣 + 剩余书的最少花费,即 = %) + (2)当 时,考虑选择 本书的最大折扣 + 剩余书的最少花费,即 = %) + …其余几种选项同理,那么对于当前的 来说,既然有这几种可能,就选择其中最少的花费作为它们的最少花费
//状态转移方程
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 查看本文章