编程之美1.6 饮料供货[动态规划vs贪心算法]

饮料供货是一个 求最优解问题。需要在给定最大容量V的前提下,从不同容量不同满意度的饮料中选择满意度最大的集合。

1. 动态规划

动态规划是最常用的解决最优化问题的方法,很容易应用到本题的需求中。用f[V,i]表示从第i,i+1,i+2,...,n-1种饮料中,算出总量为V的方案中满意度之和的最大值。

动态规划方程为:f[V,i] = max{k*Hi + f[V-k*Vi, i+1]}

弄清解法之后,我们不妨来分析一下问题的最优子结构

根据《算法导论》描述,最优子结构有两种变形:

     1) 有多少个问题在原问题的最优解中,

     2) 在决定一个最优解中使用哪些子问题时有多少个选择。

在饮料供货问题中,一个最优解只使用了一个子问题,但为了确定最优解,我们必须考虑Ci(第i中饮料可选的最大数量)种选择。

 

2. 贪心算法

书中提到第二种解法,贪心算法。由于所有饮料的容量都是2的整数幂,这就给贪心创造了条件。

假设给定的容量为V,我们可以把V写成二进制的形式,不妨设V=7,二进制的写法为111。

接下来我们就从最低位开始取:

第一步,取第一个1:拿出容量为1满意度最大的饮料,

第二步,取第二个1:使用剩余容量为1的饮料构造容量为2的饮料,比如(1,2)(1,3)构造出(2,5),并从新构造的和原有容量为2的所有饮料中选出满意度最大的。

第三步,取第三个1:递归构造容量为4的饮料,并从新构造的和原有容量为4的所有饮料中选出满意度最大的。

 

3.定义饮料类

首先需要定义一个饮料的类:

struct Drink
{
	int Volume;
	int TotalCount;
	int Happiness;
	Drink(int v, int t, int h)
	{
		Volume = v;
		TotalCount = t;
		Happiness = h;
	}
};

建立一个存储饮料的全局动态数组,用于存放所有饮料,由于同时兼顾动态规划和贪心算法两种方法,我们使用了一个二维数组。

drink[i][j]表示容量为2^i的第j个饮料。

vector<vector<Drink> > drinks;
void initialize()
{
	ifstream fin("drink.txt");
	int v,t,h;
	
	while(fin>>v>>t>>h)
	{
		while(drinks.size()<=v)
		{
			drinks.push_back(vector<Drink>());
		}
		drinks[v].push_back(Drink((int)pow(2,v),t,h));
	}
	fin.close();
}

创建样例输入drink.txt。

0 2 2
0 2 3
0 3 5
0 3 4
1 2 6
1 3 5
1 4 4
2 1 18
2 2 12

 接下来使用两种方法求解当V=7时的最高满意度值。

 

4.动态规划解法

const int MAXV=101;
const int MAXT=101;
int opt[MAXV][MAXT];

// 动态规划 
int GetMaxHappinessByDP1(int V)
{
	int maxHappiness = 0;
	vector<Drink> temp;
	for (int i=0; i<drinks.size(); ++i)
		for(int j=0; j<drinks[i].size(); ++j)
			temp.push_back(drinks[i][j]);

	int T = temp.size();

	// init
	for(int i=0; i<=T; ++i)
		opt[0][i] = 0;
	for (int i=0; i<=V; ++i)
		opt[i][T] = 0;

	for (int i=T-1; i>=0; --i)
	{
		for(int j=0; j<=V; ++j)
		{
			opt[j][i] = 0;
			for (int k=0; k<=temp[i].TotalCount; ++k)
			{
				int v = temp[i].Volume*k;
				int h = temp[i].Happiness*k;
				if (v<=j && opt[j-v][i+1]+h>opt[j][i])
				{
					opt[j][i] = opt[j-v][
  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 6
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值