编程之美 - 买书问题

问题描述:

一套书共 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;
}
    原文作者:zy__
    原文地址: https://blog.csdn.net/wangzhiyu1980/article/details/50473740
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞