编程之美 买书问题

问题描述
《哈利波特》系列一共有五卷,每一卷售价均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重循环,时间效率非常低,已经有通过数学证明总结出的规律在常数时间和空间内求解的算法。

猜你喜欢

转载自blog.csdn.net/werweqg/article/details/44233155
今日推荐