问题描述
公司采购饮料,采购的饮料有一个总量的限制 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 255
int 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