《编程之美》买书问题——动态规划

问题描述:

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

 

本数

折扣

2

5%

3

10%

4

20%

5

25%

        

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

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

 

输入样例:

1 1 1 0 0

1 1 1 1 1

 

输出样例:

21.6

30

 

思路分析:

         看到这个题目,任何人一开始想到的都是希望让书本尽可能享受高折扣。但是随着书本数目大于5本的时候,享受的总折扣就会相应出现了变化。

         举个例子, 当输入的数据为(2,2,2,1,1)的时候,如果按照享受最高折扣计算,那么对应的折扣策略就会拆分变成(1,1,1,1,1)(1,1,1,0,0)两种,总价格变为8×0.75×58×0.9×3 51.6欧元。但是如果我们变一下策略,选择4+4,购买序列变为(1,1,1,1,0)以及(1,1,1,0,1),那么总共花费8×0.8×58×0.8×551.2欧元

看到这里,我们已经可以使用动态规划通过计算总折扣数来计算最优惠价格,当然,也可以采用优化的贪心算法来实现,因为贪心算法求解这类题目都是近似解,与最优解相近。

 

解题:

先将现有条件转换一下(用百分比表示书本单价的多少倍):

 

本数

相对于书本单价的百分比

1

100%

2

190%

3

270%

4

320%

5

375%

 

具体说明一下吧:

当只有一本书的时候,没有折扣,所以为100%,即原价8欧元购买;

当有两本不同的书本,享有5%折扣,原本总价为200% ,减掉每本5%折扣,为190%,即15.2欧元;

当有三本不同的书本,享有10%折扣,原本总价为300%,减掉每本10%折扣,为270%,即21.6欧元;

当有四本不同的书本,享有20%折扣,原本总价为400%,减掉每本的20%折扣,为320%,即25.6欧元;

当有五本不同的书本,享有25%折扣,原本总价为500%,减掉每本的25%折扣,为375%,即30欧元;

以上折扣数据存放在minDis[6]中

minDIs[6] = {0 , 1.9 , 2.7 , 3.2 , 3.75 };

根据上述条件描述,我们可以定义出一条状态转移方程(核心)

F[Y1,Y2,Y3,Y4,Y5] = min{

8*minDis[5]+F(Y1-1,Y2-1,Y3-1,Y4-1,Y5-1),

8*minDis[4]+F(Y1-1,Y2-1,Y3-1,Y4-1,Y5),

8*minDis[3]+F(Y1-1,Y2-1,Y3-1,Y4,Y5),

8*minDis[2]+F(Y1-1,Y2-1,Y3,Y4,Y5),

8*minDis[1]+F(Y1-1,Y2,Y3,Y4,Y5)

}

       其中必须保证Y1>=Y2>=Y3>=Y4>=Y5,这样才不会出现更多的冗余数据。例如:(2,2,2,1,1)和(1,2,1,2,2)虽然不同,但是结果都是一样的。

#define  _CRT_SECURE_NO_WARNINGS 
#include <iostream>
#include <algorithm>
#include <functional>
using namespace std;
int main(void)
{
	double discount[6] = { 0, 1, 1.9, 2.7, 3.2, 3.75 };//存放购买不同本数的折扣
	int book[6] = { 0 };
	int bookCount = 0;//书本总类目
	int bookPrice = 8;//书本价格
	cout << "请输入五类书中每一类书的数目:" << endl;
	for (int i = 1; i <= 5; i++)
	{
		cin >> book[i];//输入各类书本的数目
	}
	sort(book + 1, book + sizeof(book) / sizeof(int), greater<int>());//保证Y1>=Y2>=Y3>=Y4>=Y5
	float arr[6][6][6][6][6] = { 0 };//存放折扣计算过程
	float minDis[6][6][6][6][6] = { 0 };//存放享有的最大折扣


	int y1 = 0;
	int y2 = 0;
	int y3 = 0;
	int y4 = 0;
	int y5 = 0;
	/*
		实现公式F(Y1,Y2,Y3,Y4,Y5) = MIN{...}  时间复杂度O(Y1×Y2×Y3×Y4×Y5) 空间复杂度为(Y1×Y2×Y3×Y4×Y5)
	*/
	for (y5 = 0; y5 <= book[5]; y5++)
	{
		for (y4 = y5; y4 <= book[4]; y4++)
		{
			for (y3 = y4; y3 <= book[3]; y3++)
			{
				for (y2 = y3; y2 <= book[2]; y2++)
				{
					for (y1 = y2; y1 <= book[1]; y1++)
					{
						double a[6] = { 0 };//存放不同购书策略所产生的临时数据
						int dir[5] = {0};//存放下一步策略的Y1 Y2 Y3 Y4 Y5
						if (y5 > 0)
						{
							dir[4] = y5 - 1;
							dir[3] = y4 - 1;
							dir[2] = y3 - 1;
							dir[1] = y2 - 1;
							dir[0] = y1 - 1;
							sort(dir, dir + 5, greater<int>());//保证Y1>=Y2>=Y3>=Y4>=Y5
							a[5] = arr[dir[0]][dir[1]][dir[2]][dir[3]][dir[4]] + discount[5] * bookPrice;
						}
						if (y4 > 0)
						{
							dir[4] = y5;
							dir[3] = y4 - 1;
							dir[2] = y3 - 1;
							dir[1] = y2 - 1;
							dir[0] = y1 - 1;
							sort(dir, dir + 5, greater<int>());
							a[4] = arr[dir[0]][dir[1]][dir[2]][dir[3]][dir[4]] + discount[4] * bookPrice;
						}
						if (y3 > 0)
						{
							dir[4] = y5;
							dir[3] = y4;
							dir[2] = y3 - 1;
							dir[1] = y2 - 1;
							dir[0] = y1 - 1;
							sort(dir, dir + 5, greater<int>());
							a[3] = arr[dir[0]][dir[1]][dir[2]][dir[3]][dir[4]] + discount[3] * bookPrice;
						}

						if (y2 > 0)
						{
							dir[4] = y5;
							dir[3] = y4;
							dir[2] = y3;
							dir[1] = y2 - 1;
							dir[0] = y1 - 1;
							sort(dir, dir + 5, greater<int>());
							a[2] = arr[dir[0]][dir[1]][dir[2]][dir[3]][dir[4]] + discount[2] * bookPrice;
						}
						if (y1 > 0)
						{
							dir[4] = y5;
							dir[3] = y4;
							dir[2] = y3;
							dir[1] = y2;
							dir[0] = y1 - 1;
							sort(dir, dir + 5, greater<int>());
							a[1] = arr[dir[0]][dir[1]][dir[2]][dir[3]][dir[4]] + discount[1] * bookPrice;
						}
						for (int i = 0; i < sizeof(a) / sizeof(double); i++)
						{
							if ((minDis[y1][y2][y3][y4][y5] > a[i] && a[i]) || minDis[y1][y2][y3][y4][y5] == 0)
								minDis[y1][y2][y3][y4][y5] = a[i];//将折扣最大的(即数字最小,但不为0的元素记录在minDIs中)
						}
						arr[y1][y2][y3][y4][y5] = minDis[y1][y2][y3][y4][y5];//将minDis赋值给arr[y1][y2][y3][y4][y5],下一轮继续使用
					}
				}
			}
		}
	}
	printf("当前购买书本可享有最低的价格为:%.2f", minDis[book[1]][book[2]][book[3]][book[4]][book[5]]);//书本享有的总优惠*书本价格*书本数目/书本数目=书本享有总优惠*书本价格

	cout << endl;
	system("pause");
	return 0;
}


  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
买书问题 dp实现 题目:买书 有一书店引进了一套书,共有3卷,每卷书定价是60元,书店为了搞促销,推出一个活动,活动如下: 如果单独购买其中一卷,那么可以打9.5折。 如果同时购买两卷不同的,那么可以打9折。 如果同时购买卷不同的,那么可以打8.5折。 如果小明希望购买第1卷x本,第2卷y本,第3卷z本,那么至少需要多少钱呢?(x、y、z为个已知整数)。 1、过程为一次一次的购买,每一次购买也许只买一本(这有种方案),或者买两本(这也有种方案), 或者本一起买(这有一种方案),最后直到买完所有需要的书。 2、最后一步我必然会在7种购买方案中选择一种,因此我要在7种购买方案中选择一个最佳情况。 3、子问题是,我选择了某个方案后,如何使得购买剩余的书能用最少的钱?并且这个选择不会使得剩余的书为负数 。母问题和子问题都是给定卷书的购买量,求最少需要用的钱,所以有"子问题重叠",问题个购买量设置为参数, 分别为i、j、k。 4、的确符合。 5、边界是一次购买就可以买完所有的书,处理方式请读者自己考虑。 6、每次选择最多有7种方案,并且不会同时实施其中多种,因此方案的选择互不影响,所以有"子问题独立"。 7、我可以用minMoney[i][j][k]来保存购买第1卷i本,第2卷j本,第3卷k本时所需的最少金钱。 8、共有x * y * z个问题,每个问题面对7种选择,时间为:O( x * y * z * 7) = O( x * y* z )。 9、用函数MinMoney(i,j,k)来表示购买第1卷i本,第2卷j本,第3卷k本时所需的最少金钱,那么有: MinMoney(i,j,k)=min(s1,s2,s3,s4,s5,s6,s7),其中s1,s2,s3,s4,s5,s6,s7分别为对应的7种方案使用的最少金钱: s1 = 60 * 0.95 + MinMoney(i-1,j,k) s2 = 60 * 0.95 + MinMoney(i,j-1,k) s3 = 60 * 0.95 + MinMoney(i,j,k-1) s4 = (60 + 60) * 0.9 + MinMoney(i-1,j-1,k) s5 = (60 + 60) * 0.9 + MinMoney(i-1,j,k-1) s6 = (60 + 60) * 0.9 + MinMoney(i-1,j,k-1) s7 = (60 + 60 + 60) * 0.85 + MinMoney(i-1,j-1,k-1)

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值