编程之美 - 买书问题
2016-01-07 09:02
337 查看
问题描述:
一套书共 5 卷,单独买每一卷的每一本8元,没有折扣。 如果一次买不同的卷的几本会有相应的折扣,折扣定义如下:
不同的 2本 : 折扣 5%
不同的 3本 : 折扣 10%
不同的 4本 : 折扣 20%
不同的 5本 : 折扣 25%
一份订单中多本书中,不同的组合可能有不同的价格,设计算法计算出最低的价格。
书中的解题思路 1:
主要也考虑的是动态规划的方式,假设 : 买5卷书的本数分别为 X1, X2, X3, X4, X5。 价格为 F(X1, X2, X3, X4, X5)。
规划一下会有下面一些可能:
每卷买一本 + 剩余部分的最小的价格
5*8*(1-25%) + F (X1-1, X2-1, X3-1, X4-1, X5-1)
有4卷买一本 + 剩余部分的最小的价格
(这里假设前X1 至 X4 卷)
4*8*(1-20%) + F (X1-1, X2-1, X3-1, X4-1, X5)
有3卷买一本 + 剩余部分的最小的价格
(这里假设前X1 至 X3 卷)
3*8*(1-10%) + F (X1-1, X2-1, X3-1, X4, X5)
有2卷买一本 + 剩余部分的最小的价格
(这里假设前X1 至 X2 卷)
2*8*(1-5%) + F (X1-1, X2-1, X3, X4, X5)
只买一本 + 剩余部分的最小的价格
(这里假设X1, 没有折扣)
8 + F (X1-1, X2, X3, X4, X5)
最后我们希望得到它们中最小的就好:
min {
5*8*(1-25%) + F (X1-1, X2-1, X3-1, X4-1, X5-1)
4*8*(1-20%) + F (X1-1, X2-1, X3-1, X4-1, X5)
3*8*(1-10%) + F (X1-1, X2-1, X3-1, X4, X5)
2*8*(1-5%) + F (X1-1, X2-1, X3, X4, X5)
8 + F (X1-1, X2, X3, X4, X5)
}
因为每本书价钱一样的,所以买每卷并不重要,重要的是每卷书在订单中的本数。
所以对 X1, X2, X3, X4, X5 排序会使问题变的简单。在上面的几种假设中,例如: 8 + F (X1-1, X2, X3, X4, X5),如果这时 X1卷已经为 0 了,此时X2卷还不为0, 那用X1-1肯定是不对的,应该是X2-1,但如果对每卷的本数都进行一次判断,肯定是非常麻烦的。
假设 Y1, Y2, Y3, Y4, Y5。是对 X1, X2, X3, X4, X5排序后的数字,且 Y1 >= Y2 >= Y3 >= Y4 >= Y5, 而每次函数 F计算的都是排序后的数字,8 + F (Y1-1, Y2, Y3, Y4, Y5) 就具有通用性了。
递归的退出条件
1) 当所有的值都为 0时肯定返回的价格就是 0 了。这是一个递归的条件。
2) 当Y1, Y2, Y3, Y4, Y5 中有一个小于0时,说明当时的方案是个非法的组合,那也就没必要再继续计算了。但此时不能返回 0,需要返回一个自定义的Max值,使当前的组合方式在min计算中失效就好。
程序代码
一套书共 5 卷,单独买每一卷的每一本8元,没有折扣。 如果一次买不同的卷的几本会有相应的折扣,折扣定义如下:
不同的 2本 : 折扣 5%
不同的 3本 : 折扣 10%
不同的 4本 : 折扣 20%
不同的 5本 : 折扣 25%
一份订单中多本书中,不同的组合可能有不同的价格,设计算法计算出最低的价格。
书中的解题思路 1:
主要也考虑的是动态规划的方式,假设 : 买5卷书的本数分别为 X1, X2, X3, X4, X5。 价格为 F(X1, X2, X3, X4, X5)。
规划一下会有下面一些可能:
每卷买一本 + 剩余部分的最小的价格
5*8*(1-25%) + F (X1-1, X2-1, X3-1, X4-1, X5-1)
有4卷买一本 + 剩余部分的最小的价格
(这里假设前X1 至 X4 卷)
4*8*(1-20%) + F (X1-1, X2-1, X3-1, X4-1, X5)
有3卷买一本 + 剩余部分的最小的价格
(这里假设前X1 至 X3 卷)
3*8*(1-10%) + F (X1-1, X2-1, X3-1, X4, X5)
有2卷买一本 + 剩余部分的最小的价格
(这里假设前X1 至 X2 卷)
2*8*(1-5%) + F (X1-1, X2-1, X3, X4, X5)
只买一本 + 剩余部分的最小的价格
(这里假设X1, 没有折扣)
8 + F (X1-1, X2, X3, X4, X5)
最后我们希望得到它们中最小的就好:
min {
5*8*(1-25%) + F (X1-1, X2-1, X3-1, X4-1, X5-1)
4*8*(1-20%) + F (X1-1, X2-1, X3-1, X4-1, X5)
3*8*(1-10%) + F (X1-1, X2-1, X3-1, X4, X5)
2*8*(1-5%) + F (X1-1, X2-1, X3, X4, X5)
8 + F (X1-1, X2, X3, X4, X5)
}
因为每本书价钱一样的,所以买每卷并不重要,重要的是每卷书在订单中的本数。
所以对 X1, X2, X3, X4, X5 排序会使问题变的简单。在上面的几种假设中,例如: 8 + F (X1-1, X2, X3, X4, X5),如果这时 X1卷已经为 0 了,此时X2卷还不为0, 那用X1-1肯定是不对的,应该是X2-1,但如果对每卷的本数都进行一次判断,肯定是非常麻烦的。
假设 Y1, Y2, Y3, Y4, Y5。是对 X1, X2, X3, X4, X5排序后的数字,且 Y1 >= Y2 >= Y3 >= Y4 >= Y5, 而每次函数 F计算的都是排序后的数字,8 + F (Y1-1, Y2, Y3, Y4, Y5) 就具有通用性了。
递归的退出条件
1) 当所有的值都为 0时肯定返回的价格就是 0 了。这是一个递归的条件。
2) 当Y1, Y2, Y3, Y4, Y5 中有一个小于0时,说明当时的方案是个非法的组合,那也就没必要再继续计算了。但此时不能返回 0,需要返回一个自定义的Max值,使当前的组合方式在min计算中失效就好。
程序代码
#include <iostream> using namespace std; #define MAX 1000000 void SortY1_Y5(int &nY1, int &nY2, int &nY3, int &nY4, int &nY5) { int nTmp = 0, nMax = 0; int i = 0, j = 0; int arrData[5] = {nY1, nY2, nY3, nY4, nY5}; for (i = 0; i < 5; i++) { nMax = arrData[i]; for (j = i; j < 5; j++) { if (arrData[j] > nMax) { nTmp = nMax; nMax = arrData[j]; arrData[j] = nTmp; } } arrData[i] = nMax; } nY1 = arrData[0]; nY2 = arrData[1]; nY3 = arrData[2]; nY4 = arrData[3]; nY5 = arrData[4]; } double minValue(double dVal1, double dVal2, double dVal3, double dVal4, double dVal5, int& nIndex) { double dmin = dVal1; nIndex = 0; if (dmin > dVal2) { nIndex = 1; dmin = dVal2; } if (dmin > dVal3) { nIndex = 2; dmin = dVal3; } if (dmin > dVal4) { nIndex = 3; dmin = dVal4; } if (dmin > dVal5) { nIndex = 4; dmin = dVal5; } return dmin; } // Y1 >= Y2 >= Y3 >= Y4 >= Y5 double Calc(int nY1, int nY2, int nY3, int nY4, int nY5) { double dMin = 0; int nIndex = 0; if ((nY1 == 0) && (nY2 == 0) && (nY3 == 0) && (nY4 == 0) && (nY5 == 0)) return 0; if ((nY1 - 1 >= 0) || (nY2 - 1 >= 0) || (nY3 - 1 >= 0) || (nY4 - 1 >= 0) || (nY5 - 1 >= 0)) { SortY1_Y5(nY1, nY2, nY3, nY4, nY5); dMin = minValue( (5*8)*(1-0.25) + Calc(nY1-1, nY2-1, nY3-1, nY4-1, nY5-1), (4*8)*(1-0.20) + Calc(nY1-1, nY2-1, nY3-1, nY4-1, nY5), (3*8)*(1-0.10) + Calc(nY1-1, nY2-1, nY3-1, nY4, nY5), (2*8)*(1-0.05) + Calc(nY1-1, nY2-1, nY3, nY4, nY5), 8 + Calc(nY1-1, nY2, nY3, nY4, nY5), nIndex); } else { return MAX; } switch (nIndex) { case 0: cout << "Min: " << dMin << "( " <<nY1-1 << ", " << nY2-1 << ", " << nY3-1 << ", " << nY4-1 << ", " << nY5-1 << ", " << ")"<< endl; break; case 1: cout << "Min: " << dMin << "( " <<nY1-1 << ", " << nY2-1 << ", " << nY3-1 << ", " << nY4-1 << ", " << nY5 << ", " << ")"<< endl; break; case 2: cout << "Min: " << dMin << "( " <<nY1-1 << ", " << nY2-1 << ", " << nY3-1 << ", " << nY4 << ", " << nY5 << ", " << ")"<< endl; break; case 3: cout << "Min: " << dMin << "( " <<nY1-1 << ", " << nY2-1 << ", " << nY3 << ", " << nY4 << ", " << nY5 << ", " << ")"<< endl; break; case 4: cout << "Min: " << dMin << "( " <<nY1-1 << ", " << nY2 << ", " << nY3 << ", " << nY4 << ", " << nY5 << ", " << ")"<< endl; break; default: break; } return dMin; } void main() { double dMoney = 0; //int test[5] = {1,1,1,1,1}; //int test[5] = {2,2,2,2,2}; //int test[5] = {1,1,0,0,0}; //int test[5] = {2,2,2,1,1}; int test[5] = {3,2,2,1,1}; //int test[5] = {2,1,1,0,0}; dMoney = Calc(test[0], test[1], test[2], test[3], test[4]); cout << dMoney << endl; cin >> dMoney; }
相关文章推荐
- java160104KeyWordDemo
- java160104PackageDemo
- C#之浏览器请求跨域
- java160104DemoA
- 在oc语言中字符串和整型数相互转换
- python之路--线程和进程
- java160104DemoB
- java160104DemoC
- java160102PackageDemo
- POI解析Excel(.xls)和Excel2007(.xlsx)
- java160102DemoA
- java160102DemoB
- [javase学习笔记]-8.8 构造代码块
- JDK SDK NDK ANT ADT
- Spring MVC - 开发 Controller的特性与乱码问题
- 学习笔记(六):C++串口连接
- 我整理的PHP 7.0主要新特性
- Java日期加减操作
- C# 读写文件(StreamRead,StreamWrite,File,FileStream)
- 对Java的面对对象编程中对象和引用以及内部类的理解