饮料供货问题

题目:在微软亚洲研究院上班,大家早上来的第一件事是干啥呢?查看邮件?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];
	}
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值