题目:在微软亚洲研究院上班,大家早上来的第一件事是干啥呢?查看邮件?No,是去水房拿饮料:酸奶,豆浆,绿茶、王老吉、咖啡、可口可乐……(当然,还是有很多同事把拿饮料当做第二件事)。
管理水房的阿姨们每天都会准备很多的饮料给大家,为了提高服务质量,她们会统计大家对每种饮料的满意度。一段时间后,阿姨们已经有了大批的数据。某天早上,当实习生小飞第一个冲进水房并一次拿了五瓶酸奶、四瓶王老吉、三瓶鲜橙多时,阿姨们逮住了他,要他帮忙。
从阿姨们统计的数据中,小飞可以知道大家对每一种饮料的满意度。阿姨们还告诉小飞,STC(Smart Tea Corp.)负责给研究院供应饮料,每天总量为V。STC很神奇,他们提供的每种饮料之单个容量都是2的方幂,比如王老吉,都是23=8升的,可乐都是25=32升的。当然STC的存货也是有限的,这会是每种饮料购买量的上限。统计数据中用饮料名字、容量、数量、满意度描述每一种饮料。
那么,小飞如何完成这个任务,求出保证最大满意度的购买量呢?
解法一: 动态规划
先进行数学建模:
假设STC共提供n种饮料,用(Si, Vi, Ci,Hi,Bi)(对应饮料名、容量、可能的最大数量、满意度、实际购买量)来表示第i种饮料(i =0,1,。。,n-1),其中可能的最大数量指如果仅买某种饮料的最大可能数量。
基于如上公式:饮料总容量为 V1*B1+V2*B2+...+Vn*Bn 总满意度为H1*B1+H2*B2+...+Hn*Bn
那么题目的要求就是,在满足V1*B1+V2*B2+...+Vn*Bn=V的基础上,求解max(H1*B1+H2*B2+...+Hn*Bn)
用Opt(V', i) 表示从第i,i+1,i+2,...,n-1种饮料中,算出总量为V'的方案中满意度之和的最大值。因此,Opt(V,0)就是我们要求的值。
那么,可以列出如下的推导公式:Opt(V',i) = max {k*Hi + Opt(V'-k*Vi, i+1)} k=0,1,..., Ci, i=0, 1, ..., n-1
初始边界条件: Opt(0, n) = 0 容量为0的情况下,最优化结果为0。
Opt(x, n) = -INF (x!=0) 容量不为0的情况下,把最优化结果设为负无穷大,并把它作为初值。
根据推导公式,动态规划求解代码如下:
/*
* Drink.h
*
* Created on: 2014年2月8日
* Author: Eileen
*/
#ifndef DRINK_H_
#define DRINK_H_
namespace std {
class Drink {
public:
Drink();
virtual ~Drink();
int Cal(int*, int*, int*);
static const int N = 3; //饮料种数
static const int V = 8; //总容量数
int V[] = {1, 1, 1};
int H[] = {1, 10, 100};
int C[] = {10, 2, 2};
int (*result)[N+1] = new int[V+1][N+1];
void PrintDrink(int*);
};
} /* namespace std */
#endif /* DRINK_H_ */
/*
* Drink.cpp
*
* Created on: 2014年2月8日
* Author: Eileen
*/
#include "Drink.h"
#include <iostream>
using namespace std;
Drink::Drink() {
// TODO Auto-generated constructor stub
for(int i=0; i<=V; i++)
for(int j=0; j<=N; j++)
{
result[i][j] = 0;
}
}
Drink::~Drink() {
// TODO Auto-generated destructor stub
}
int Drink::Cal(int* VArray, int* HArray, int* CArray){
//VArray为容量数组, HArray为满意度数组, CArray为最大数量数组
//V为总容量数,N为饮料总数
int (*opt)[N+1] = new int[V+1][N+1];
opt[0][N] = 0;
for (int i=0; i<=V; i++)
for (int j=0; j<=N; j++)
{
opt[i][j] = INF;
}
for(int i=N-1; i>=0; i--)
{
for(int j=1; j<=V; j++)
{
for(int k=0; k<=CArray[i];k++)
{
if(j < k*VArray[i])
break;
if((k*HArray[i]+opt[j-k*VArray[i]][i+1])>opt[j][i])
{
opt[j][i] = k*HArray[i]+opt[j-k*VArray[i]][i+1];
result[j][i] = k;
}
}
}
}
return opt[V][0];
}
void Drink::PrintDrink(int* VArray){
int V1=V;
for(int i=0; i<=N-1; i++){
cout<<result[V1][i]<< " ";
V1 -= result[V1][i]*VArray[i];
}
}