问题描述:
一套书共 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;
}