问题描述
《哈利波特》系列一共有五卷,每一卷售价均8欧元。同时买不同的卷(各一本)有折扣,具体如下表所示。
购买方案 折扣
2卷 5%
3卷 10%
4卷 20%
5卷 25%在一份订单中,根据购买的卷数及本数,可以有多种不同折扣规则。但一本书只能应用一个折扣规则。
设计一个算法,计算购书组合,使得所购买的一批书花费最少。
求解及分析
假设输入为:1本卷1,2本卷2,2本卷3,2本卷4,1本卷5
采用动态规划,任意时候,F(X1 , X2, X3 ,X4 ,X5)均满足X1>=X2>=X3>=X4>=X5
转移方程
F(X1,X2,X3,X4,X5)=0 ; //X1=X2=X3=X4=X5=0
F(X1,X2,X3,X4,X5)= min{
5*8*(1-25%) +F(X1-1,X2-1,X3-1,X4-1,X5-1) //x5 > 0
4*8*(1-20%) +F(X1-1,X2-1,X3-1,X4-1,X5) //x4 > 0
3*8*(1-10%) +F(X1-1,X2-1,X3-1,X4,X5) //x3 > 0
2*8*(1-5%) +F(X1-1,X2-1,X3,X4-1,X5-1) //x2> 0
8 +F(X1-1,X2,X3,X4,X5-1) //x1 > 0
}
#include<iostream>
#include<vector>
#include <algorithm>
#include<map>
#include<sstream>
#define INF 10000000
using namespace std;
bool cmp(int a, int b)
{
return a>b;
}
int min(int a, int b)
{
return a < b ? a : b;
}
//将索引前n位减1后再归一化 即用最小化法表示
void scalarIndex(const vector<int>& orign, vector<int>& result, int n)
{
result = orign;
for (int i = 0; i < n; i++)
{
result[i] -= 1;
}
sort(result.begin(), result.end(), cmp);
}
//将当前索引转换为关键字
string getKey(const vector<int> & index)
{
stringstream ss;
string str = "";
for (int i = 0; i < index.size(); i++)
{
ss << index[i];
}
ss >> str;
return str;
}
double buyBookWay( const vector<int>& books)
{
map<string, double> cost; //使用map类型记录状态
double discount[5] = { 1, 0.95, 0.9, 0.8, 0.75 };
vector<int>innerBooks = books;
int count[5] = { 0, 0, 0, 0, 0 };
cost["00000"] = 0;
sort(innerBooks.begin(), innerBooks.end(), cmp);
for (count[0] = 0; count[0] <innerBooks[4] + 1; ++count[0])
{
for (count[1] = count[0]; count[1] <innerBooks[3] + 1; ++count[1])
{
for (count[2] = count[1]; count[2] <innerBooks[2] + 1; ++count[2])
{
for (count[3] = count[2]; count[3] <innerBooks[1] + 1; ++count[3])
{
for (count[4] = count[3]; count[4]<innerBooks[0] + 1; ++count[4])
{
vector<int> index;
for (int i = 0; i < 5; i++)
{
index.push_back(count[i]);
}
sort(index.begin(), index.end(), cmp);//将当前索引以最小化法表示
cout << getKey(index)<<endl;
if (index[0] < 1) continue; //如果为F[0][0][0][0][0] 则返回
double currCost = INF; //将当前费用初始化为无穷大
vector<int> transferIndex; //用于状态转移方程内的索引
for ( int i = 0; i < 5; i++) //状态转移方程 求取当前索引对应的最优化费用
{
if (index[i] >= 1)
{
scalarIndex(index, transferIndex, i + 1);
double temp = (i + 1) * 8 * discount[i] + cost[getKey(transferIndex)];
currCost = min(currCost, temp);
}
}
cost[getKey(index)] =currCost;
}
}
}
}
}
return cost[getKey(innerBooks)];
}
int main()
{
vector<int> myBooks = { 2, 2, 2, 2, 1 };
cout <<"总价为: "<< buyBookWay(myBooks);
system("pause");
return 0;
}
算法评价:
由于存在5重循环,时间效率非常低,已经有通过数学证明总结出的规律在常数时间和空间内求解的算法。