单位质量最大价值问题

描述:

有n个物品,单个物品的质量为w(weigh),价值为v(value),要从n个物品中选取k个,求再所有的选取方案中,能获得的最大单位质量价值。也就是k个物品总的价值V / 总的质量W。保留两位小数。


说明:

一般最先想到的方法是把每个物品按照单位价值排序,从大到小贪心进行选取。但这种方法是不正确的。举一个反例就好了。

比如说:n = 3, k = 2,(w, v) = {(2,2),(5,3),(2,1)},按照之前的策略应该选第一和第二物品,得到的平均价值为5/7 = 0.714。

但如果我们选择第一和第三个物品的话,结果就是3/4 = 0.75。所以说要想别的办法。

定义条件 C(x):= 可以选择使得单位重量的价值不小于x

原问题变成了求满足C(x)的最大x的问题。如何判断C(x)为真呢,假设我们选了k个物品,组成的集合为S,那么它们的单位质量价值是 Σv / Σw。

因此就变成了判断是否存在S满足条件 Σv / Σw >= x,变形得到 Σ(vi - x * wi) >= 0 。

因此,对vi - x * wi 的值进行排序,贪心的进行选取,最后问题变成了

C(x):((vi - x * wi) 从大到小排列中前k个和不小于0)   每次判断排序依次复杂度O(nlogn)。


完整实现代码如下:

#include <iostream>
#include <algorithm>

using namespace std;

const int MAX_N = 1000;
const double MAX_X = 1000000.0;
int n, k;
int w[MAX_N], v[MAX_N];
double y[MAX_N];	// v - x * w

bool C(double x)
{
	for (int i = 0; i < n; i++)
		y[i] = v[i] - x * w[i];
	sort(y, y + n);

	// 计算y数组中从大到小前k个数的和
	double sum = 0.0;
	for (int i = 0; i < k; i++)
		sum += y[n - 1 - i];
	return sum >= 0;
}

int main()
{
	cin >> n >> k;
	for (int i = 0; i < n; i++)
		cin >> w[i] >> v[i];
	
	double lb = 0.0, ub = MAX_X;
	for (int i = 0; i < 100; i++)
	{
		double mid = (lb + ub) / 2;
		if (C(mid))
			lb = mid;
		else
			ub = mid;
	}
	printf("%.2f\n", ub);
}

结果:

单位质量最大价值问题_第1张图片

ps:这个主要在《挑战程序设计竞赛》上看到的,感觉非常巧妙。搬上来跟大家分享下。也强烈推荐这本书。

你可能感兴趣的:(单位质量最大价值问题)