平均值的最值化 - 二分

模型:有\(n\)个物品,第\(i\)个物品重量为\(w_i\),价值为\(v_i\)。从中选\(k\)个物品,使得单位重量的价值最大。

sol:
\(C(x)\)为“是否可以选择\(k\)个物品,使得单位重量的价值不小于\(x\)”。
这个东西显然是关于\(x\)单调的,所以我们二分之。
设选择的集合为\(S\)
由于

\[x \ge\frac{\sum_{j \in S}{v_i}}{\sum_{j\in S}{w_i}} \]

所以

\[\sum_{j \in S}{(v_i - w_i\times x)} \ge 0 \]

所以按照\(key_i = (v_i - w_i\times x)\)的顺序从大到小选取即可,\(C(x)\)就转化为前\(k\)个的\(key_i\)和是否不小于0。

最小化平均值的问题同理解决之。

例题:POJ 2976,3111,3258,3273,3104,3045。

附POJ 3111的程序

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

 #define rep(i,a,b) for (int i = a; i <= b; i++)

 inline int read() {
 	char ch = getchar(); int x = 0;
 	while (ch < '0' || ch > '9') ch = getchar();
 	while (ch > '0' && ch < '9') {
 		x = x*10 + ch - 48;
 		ch = getchar();
 	}
 	return x;
 }

 const int N = 100000 + 5;

 int n, k;

 struct Data { int id; double v, w, key; } jewel[N];

 bool cmp(Data A, Data B) { return A.key > B.key; }

 bool judge(double x) {
 	rep(i,1,n) jewel[i].key = double(jewel[i].v) - x*double(jewel[i].w);
 	sort(jewel+1, jewel+n+1, cmp);
 	double sum = 0;
    for (int i = 1; i <= k && sum >= 0; i++) sum += jewel[i].key;
    return (sum >= 0);
 }

int main()
{	
	while (scanf("%d%d", &n, &k) == 2) {
		rep(i,1,n) scanf("%lf%lf", &jewel[i].v, &jewel[i].w), jewel[i].id = i;
		double l = 0.0, r = 1.0 * 0x3f3f3f3f;
		rep(i,1,100) {
			double mid = (l + r)/2;
			if (judge(mid)) l = mid; else r = mid;
		}
		rep(i,1,k-1) printf("%d ", jewel[i].id);
		printf("%d\n", jewel[k].id);
	}
	return 0;
}

有一个常数优化的余地:排序的时候不交换整个struct,而是只排序下标。

posted @ 2016-09-24 22:41  Armeria  阅读(195)  评论(0编辑  收藏  举报