1. 问题以及扩展
本题所说的问题是微软每天为员工提供各种不同的饮料,如可乐,酸奶,豆浆,咖啡,绿茶……..(待遇不错啊,呵呵),饮料i的单位容量为Vi,其中每种饮料单个容量都是2的方幂,员工对饮料i的满意度为Hi,冰柜的总容量为V(每天必须装满),问题是如何组合现有的各种饮料,使总的满意度最高。
已知每种饮料的名字、容量、数量以及满意度,求总容量V一定的情况下的最大满意度。
扩展:
(1) 如何度量每一种饮料的满意度,简单的平均,则不一定使得所有人的满意度之和达到最高!
(2) 每个人都有一个满意度量,则这时候如何计算总的最大满意度?
注:遗憾的是自己提出的扩展问题却解决不了,期待高人指点。
2. 数据的产生
针对该问题,随机生成了100种产品,实现代码如下所示:
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <math.h>
#include <fstream>
using namespace std;
#define MAXNUM 5
void generData()
{
ofstream outfile("Data.txt");
int drinkID = 0;
int volumn = 0;
int quantity = 0;
float satisfaction = 0.0;
srand((unsigned)time(NULL));
for (drinkID=0; drinkID<100; drinkID++)
{
volumn = pow(2, rand()%7);
quantity = rand()%10+1;
satisfaction = 1.0/(1 + rand()%20);
outfile<<drinkID<<" "<<volumn<<" "<<quantity<<" "<<satisfaction<<endl;
}
outfile.close();
}
int main(void)
{
generData();
return 1;
}
3. 求解
3.1 动态规划
最优化公式:
Opt(v',i) = max{max{k*Hi + Opt(v'-vi*k, i-1)}, Opt(v', i-1)}
其中v'表示容量,k表示购买第i种饮料的数量,Hi是第i种饮料的满意度。初始条件:Opt(j, 0) = 0表示任何容量j选择0种饮料时满意度均为零。Opt(v, n)为最终求解的结果。
实现代码如下:
#include <iostream>
#include <fstream>
#include <string>
#include <stdio.h>
#include <stdlib.h>
using namespace std;
#define DRINKKINS 100
#define TOTALVOLUMN 14
class drink
{
public:
int id;
int volumn;
int count;
float satisfaction;
drink()
{
}
drink(int newid, int newvolumn, int newcount, float newsatisfaction)
{
id = newid;
volumn = newvolumn;
count = newcount;
satisfaction = newsatisfaction;
}
~drink()
{
}
inline void set(int newid, int newvolumn, int newcount, float newsatisfaction)
{
id = newid;
volumn = newvolumn;
count = newcount;
satisfaction = newsatisfaction;
}
void outdrink()
{
cout<<id<<" "<<volumn<<" "<<count<<" "<<satisfaction<<endl;
}
};
void cal (float Optimal[DRINKKINS+1][TOTALVOLUMN+1], drink drinks[DRINKKINS+1])
{
for (int i=0; i<=TOTALVOLUMN; ++i)
{
Optimal[0][i] = 0;
}
for (int i=1; i<= DRINKKINS; i++)
{
for (int j=0; j<=TOTALVOLUMN; ++j)
{
Optimal[i][j] = Optimal[i-1][j];//初始值
for (int k=1; k<=drinks[i].count && j>= k*drinks[i].volumn; ++k)
{
float tempOpt = k*drinks[i].satisfaction + Optimal[i-1][j-k*drinks[i].count];
Optimal[i][j] = Optimal[i][j]>tempOpt ? Optimal[i][j]:tempOpt;
}
}
}
}
int main()
{
ifstream fin("Data.txt");//Data.txt为存储数据的文件
char s1[10], s2[10], s3[10], s4[10];
drink drinks[DRINKKINS+1];
int i = 1;
while (fin>>s1, fin>>s2, fin>>s3, fin>>s4)
{
drinks[i++].set(atoi(s1), atoi(s2), atoi(s3), atof(s4));
}
fin.close();
//注意矩阵第一行的数据均应为零,表示虽然容量有,但是第0种饮料是不存在的,只是虚拟方便计算
float Optimal[DRINKKINS+1][TOTALVOLUMN+1];
cal(Optimal, drinks);
cout<<"The Maxmum satisfaction is :"<<Optimal[DRINKKINS][TOTALVOLUMN]<<endl;
return 1;
}
3.2 贪心
基于容量的贪心实现比较麻烦,例如总容量V=7=1+2+4,显然我们可以选择容量分别为1,2,4中满意度最高的。但4=2+2,即我们可以选择容量分别为1,2,2,2的饮料从而获得更高的满意度,当然我们可以将4=2+2也当作容量为4的进行贪心,可是我们无法确定我们选择此方案时是否构成该满意度的两种(或一种)饮料是否还有这么多容量。
个人理解:贪心是为了简化问题的求解,显然这时候用谈心会增大问题的求解难度,认为不好!不知道是否存在高效的实现方法?思考中……