编程之美 - 饮料供货

来源:互联网 发布:淘宝外包费用 编辑:程序博客网 时间:2024/04/29 10:24

问题描述

公司采购饮料,采购的饮料有一个总量的限制 V0升,同时每种饮料有最大瓶数的限制C(i),大家对每种饮料有一个满意度 H(i),问怎样采购能使总的满意度最高。注: 饮料的包装规格都是2的n次方的整数。


想法 I : 动态规划

假设:

          饮料的种类为 n
          每种饮料的购买量为 B(i)  (i  = 0.. n)
          每种饮料最大瓶数的限制  C(i)  (i  = 0.. n)
          每种饮料的满意度为  H(i)  (i  = 0.. n)
          每种饮料的包装规格  V(i)  (i  = 0.. n)
          总的 容量为 V0
          满意度为  Happy

V0 = B(0) * V(0) + B(1) * V(1) + .... + B(n) * V(n)
Happy = B(0) * H(0) + B(1) * H(1) + .... + B(n) * H(n)

求的是 Max(Happy)。

一个最优的结果  Opt(V0,n),是由中间若干个步骤的最优结果 Opt(V`,i) 推算出来的
Opt(V`,i) = MAX{  B(i)*H(i) + Opt(V`- B(i)*V(i), i-1)  }

当前步骤的最优结果 = 上一步的最优结果 + 当前步骤的结果。
对每一步都进行最优结果的推算,最后得到的一定是最优的方案。

举例:   有3种饮料,  总的容量 为  8
每一种的瓶数限制  arrC[] = {5, 4, 6};
每一种的满意度     arrH[] = {3, 5, 2};
每种的容量            arrV[] = {2, 4, 8}

代码示例:
#include <iostream>#include <iomanip> using namespace std;#define V0 8#define T  3#define INF 255int arrC[] = {5, 4, 6};int arrH[] = {3, 5, 2};int arrV[] = {2, 4, 8};void printOpt(int arr[V0+1][T+1], int nLeni, int nLenj){    int i =0, j = 0;    cout << "=================================" << endl;    for (i = 0; i < nLeni; i++)    {        for (j = 0; j < nLenj; j++)        {            cout.fill(' ');              cout.setf(ios::right);              cout.width(6);              cout << setprecision(6) << arr[i][j] << "  ";        }        cout << endl;    }    cout << endl;    cout << endl;}int Calc(int V){    int i=0, j=0, k=0;    int opt[V0+1][T+1];    memset(opt, 0, sizeof(int) * (V0+1)*(T+1));    opt[0][T] = 0;    for (i = 1; i <= V0; i++)    {        opt[i][T] = -INF;    }    printOpt(opt, V0+1, T+1);    for (j = T-1; j >=0; j--)    {        for (i = 0; i <= V0; i++)        {            opt[i][j] = -INF;            for (k = 0; k < arrC[j]; k++)            {                if (i < k * arrV[j])                    break;                int h = opt[i - k * arrV[j]][j+1];                if (h != -INF)                {                    h += arrH[j] * k;                    if (h > opt[i][j])                    {                        opt[i][j] = h;                        printOpt(opt, V0+1, T+1);                    }                }            }        }    }    printOpt(opt, V0+1, T+1);    return opt[V0][0];}void main(){    int nHappy = 0;    nHappy = Calc(V0);    cout << nHappy << endl;    cin >> nHappy;}


运算结果:

=================================    初始化状态
     0       0       0       0
     0       0       0    -255
     0       0       0    -255
     0       0       0    -255
     0       0       0    -255
     0       0       0    -255
     0       0       0    -255
     0       0       0    -255
     0       0       0    -255


=================================
     0       0       0       0
     0       0       0    -255
     0       0       0    -255
     0       0       0    -255
     0       0       0    -255
     0       0       0    -255
     0       0       0    -255
     0       0       0    -255
     0       0       0    -255


=================================   买第3种饮料,买8升得到的 Happy
     0       0       0       0                                                    opt(8, 2) = 2
     0       0    -255    -255
     0       0    -255    -255
     0       0    -255    -255
     0       0    -255    -255
     0       0    -255    -255
     0       0    -255    -255
     0       0    -255    -255
     0       0       2    -255


=================================
     0       0         0         0
     0       0    -255    -255
     0       0    -255    -255
     0       0    -255    -255
     0       0    -255    -255
     0       0    -255    -255
     0       0    -255    -255
     0       0    -255    -255
     0       0         2    -255


=================================  买第2种饮料,买4升得到的 Happy = 5
     0         0         0         0                                             opt(4, 1) = 5
     0    -255    -255    -255
     0    -255    -255    -255
     0    -255    -255    -255
     0         5    -255    -255
     0         0    -255    -255
     0         0    -255    -255
     0         0    -255    -255
     0         0          2    -255


=================================  买第2种饮料,买8升时要先和之前的第三种饮料的 8升做个比较
     0         0         0         0                                             此时第三种的饮料8升的 Happy 为 2
     0    -255    -255    -255
     0    -255    -255    -255
     0    -255    -255    -255
     0         5    -255    -255
     0    -255    -255    -255
     0    -255    -255    -255
     0    -255    -255    -255
     0          2         2    -255                                        


=================================  买第2种饮料,买8升时要先和之前的第三种饮料的 8升做个比较
     0          0         0         0                                            买第二种的饮料8升的 Happy 为 10,  10作为最优解被保留
     0    -255    -255    -255
     0    -255    -255    -255
     0    -255    -255    -255
     0          5    -255    -255
     0    -255    -255    -255
     0    -255    -255    -255
     0    -255    -255    -255
     0        10         2    -255                                           opt(8, 2) = 2  vs  opt(8, 1) = 10


=================================
     0          0         0         0
     0    -255    -255    -255
     0    -255    -255    -255
     0    -255    -255    -255
     0          5    -255    -255
     0    -255    -255    -255
     0    -255    -255    -255
     0    -255    -255    -255
     0        10         2    -255


=================================  买第1种饮料,买2升得到的 Happy = 3
     0            0         0          0
  -255    -255    -255    -255
       3    -255    -255    -255                                         opt(2, 0) = 3
       0    -255    -255    -255
       0          5    -255    -255
       0    -255    -255    -255
       0    -255    -255    -255
       0    -255    -255    -255
       0        10         2    -255


================================= 买第1种饮料,买4升,要和 买第2种饮料 买4升的happy进行比较
     0            0         0         0  
  -255    -255    -255    -255
       3     -255    -255    -255
  -255    -255    -255    -255
       5          5    -255    -255                                        opt(4, 1) = 5
       0    -255    -255    -255
       0    -255    -255    -255
       0    -255    -255    -255
       0        10         2    -255


=================================   opt(4, 0) = 6  第1种 4升的方案被保留
     0            0         0         0
  -255    -255    -255    -255
       3     -255    -255    -255
  -255    -255    -255    -255
       6          5    -255    -255                                          opt(4, 1)  vs  opt(4, 0)  6 > 5    6 被保留
       0    -255    -255    -255
       0    -255    -255    -255
       0    -255    -255    -255
       0        10         2    -255


=================================    取6升时,可以取2升第1种饮料和取4升第2种,他们的
     0            0         0         0                                            Happy 和 = 3 + 5 = 8
  -255    -255    -255    -255
       3     -255    -255    -255
  -255    -255    -255    -255
       6          5    -255    -255
   -255    -255   -255    -255
       8     -255    -255    -255
       0     -255    -255    -255
       0         10         2    -255


=================================    取6升时,也可以取6升第1种饮料 Happy = 3 * 3 = 9
     0            0         0         0
  -255    -255    -255    -255
       3     -255    -255    -255
  -255    -255    -255    -255
        6         5    -255    -255
   -255    -255    -255    -255
        9    -255    -255    -255                
        0    -255    -255    -255
        0        10         2    -255


=================================
     0            0         0         0
  -255    -255    -255    -255
       3     -255    -255    -255
  -255    -255    -255    -255
       6          5    -255    -255
   -255    -255    -255    -255                                           win
        9     -255    -255    -255                                        opt(6, 0)  vs  opt(2, 0) + opt(4, 1)  
   -255    -255    -255    -255
       10       10         2    -255


=================================  取6升时,可以取4升第1种饮料和取4升第2种,他们的
     0            0         0         0                                          Happy 和 = 6 + 5 = 11
  -255    -255    -255    -255
       3     -255    -255    -255
  -255    -255    -255    -255
       6          5    -255    -255
   -255    -255    -255    -255
        9     -255    -255    -255
   -255    -255    -255    -255
       11       10         2    -255


=================================  取6升时,也可以取8升第1种饮料 Happy = 3 * 4 = 12
     0            0         0         0
  -255    -255    -255    -255
       3     -255    -255    -255
  -255    -255    -255    -255
       6          5    -255    -255
   -255    -255    -255    -255
        9     -255    -255    -255
   -255    -255    -255    -255
       12       10         2    -255


=================================
     0            0         0         0
  -255    -255    -255    -255
       3     -255    -255    -255
  -255    -255    -255    -255
       6          5    -255    -255
   -255    -255    -255    -255
        9     -255    -255    -255                         
   -255    -255    -255    -255                                          win
       12       10         2    -255                                       opt(8, 0)  vs  opt(4, 0) + opt(4, 1) 


12

空间复杂度:使用了一个二维数组 V * T -->  总的升数 * 总的饮料种类
时间复杂度:使用了3 个for 循环嵌套  V * T * Max(Ci)   -->  总的升数 * 总的饮料种类 * 找到当前种类饮料最大解的时间


想法 II : 动态规划方式的一种变形,通过增加一个状态标记,判断当前升数-种类的饮料是否被计算过来提高效率。
用 -1 标记当前升数,当前种类的饮料-当前升数,还没有被计算过。用-INF表示当前升数-当前种类的饮料是无效的。


程序示例:
#include <iostream>#include <iomanip> using namespace std;#define V0 8#define T  3#define INF 255//int arrC[] = {5, 4, 6};//int arrH[] = {3, 5, 2};//int arrV[] = {2, 4, 8};int arrC[] = {5, 4, 6};int arrH[] = {1, 5, 2};int arrV[] = {2, 4, 8};void printOpt(int arr[V0+1][T+1], int nLeni, int nLenj){    int i =0, j = 0;    cout << "=================================" << endl;    for (i = 0; i < nLeni; i++)    {        for (j = 0; j < nLenj; j++)        {            cout.fill(' ');              cout.setf(ios::right);              cout.width(6);              cout << setprecision(6) << arr[i][j] << "  ";        }        cout << endl;    }    cout << endl;    cout << endl;}int opt[V0+1][T+1];int calc(int V, int type){    int nRet = -INF;    int i = 0;    if (type == T)    {        if (V == 0)            return 0;        else            return -INF;    }    if (V < 0)        return -INF;    else if (V == 0)        return 0;    else if (opt[V][type] != -1)        return opt[V][type];    for (i = 0; i < arrC[type]; i++)    {        if (V - i * arrV[type] < 0)            continue;        int nTmp = calc(V - i * arrV[type], type+1);        if (nTmp != -INF)        {            nTmp += arrH[type] * i;            if (nTmp > nRet)            {                nRet = nTmp;            }        }    }    opt[V][type] = nRet;    printOpt(opt, V0+1, T+1);    return nRet;}void main(){    int i = 0;    memset(opt, -1, 4*(V0+1)*(T+1));    i = calc(V0, 0);    cout << i;    cin >> i;}


测试结果:

=================================
    -1      -1      -1      -1
    -1      -1      -1      -1
    -1      -1      -1      -1
    -1      -1      -1      -1
    -1      -1      -1      -1
    -1      -1      -1      -1
    -1      -1      -1      -1
    -1      -1      -1      -1
    -1      -1       2      -1


=================================
    -1      -1      -1      -1
    -1      -1      -1      -1
    -1      -1      -1      -1
    -1      -1      -1      -1
    -1      -1    -255      -1
    -1      -1      -1      -1
    -1      -1      -1      -1
    -1      -1      -1      -1
    -1      -1       2      -1


=================================
    -1      -1      -1      -1
    -1      -1      -1      -1
    -1      -1      -1      -1
    -1      -1      -1      -1
    -1      -1    -255      -1
    -1      -1      -1      -1
    -1      -1      -1      -1
    -1      -1      -1      -1
    -1      10       2      -1


=================================
    -1      -1      -1      -1
    -1      -1      -1      -1
    -1      -1      -1      -1
    -1      -1      -1      -1
    -1      -1    -255      -1
    -1      -1      -1      -1
    -1      -1    -255      -1
    -1      -1      -1      -1
    -1      10       2      -1


=================================
    -1      -1      -1      -1
    -1      -1      -1      -1
    -1      -1    -255      -1
    -1      -1      -1      -1
    -1      -1    -255      -1
    -1      -1      -1      -1
    -1      -1    -255      -1
    -1      -1      -1      -1
    -1      10       2      -1


=================================
    -1      -1      -1      -1
    -1      -1      -1      -1
    -1      -1  -255      -1
    -1      -1      -1      -1
    -1      -1  -255      -1
    -1      -1      -1      -1
    -1    -255  -255    -1
    -1      -1      -1      -1
    -1      10       2      -1


=================================
    -1      -1      -1      -1
    -1      -1      -1      -1
    -1      -1    -255    -1
    -1      -1      -1      -1
    -1       5    -255    -1
    -1      -1      -1      -1
    -1    -255  -255    -1
    -1      -1      -1      -1
    -1      10       2      -1


=================================
    -1      -1      -1      -1
    -1      -1      -1      -1
    -1    -255  -255    -1
    -1      -1      -1      -1
    -1       5    -255     -1
    -1      -1      -1      -1
    -1    -255  -255    -1
    -1      -1      -1      -1
    -1      10       2      -1


=================================
    -1      -1      -1      -1
    -1      -1      -1      -1
    -1    -255  -255    -1
    -1      -1      -1      -1
    -1       5    -255    -1
    -1      -1      -1      -1
    -1    -255  -255    -1
    -1      -1      -1      -1
    10      10       2      -1


10




0 0
原创粉丝点击