二分算法的應用——最大化平均值
阿新 • • 發佈:2018-02-25
sum turn 從大到小 div std pin 方法 while ont
最大化平均值
有n個物品的重量和價值分別wi 和 vi。從中選出 k 個物品使得 單位重量 的價值最大。
限制條件:
1 <= k <= n <= 10^4
1 <= w_i <= v_i <= 10^6
輸入:
n = 3
k = 2
{W, V} = {(2,2), (5,3), (2,1)}
輸出:
0.75 (如果選0號和2號,平均價格是 (2 + 1) / (2 + 2) = 0.75)
題解:
一般先想到的肯定是:把物品按照 單位價值 進行排序,然後從大到小貪心地進行選取。但是這個方法對應輸入得到的 是 5/7=0.714。不可行。
轉換成二分搜索的問題,由之前的博客中,這種題目關鍵就是 編寫二分的條件C(x)。
- C(x) = 可以選擇使得 單位重量的價格 不小於 x
假設 n組數據,那他們 單位重量的價格是:
- sum(vi) / sum(wi)
因此就變成了:
- sum(vi) / sum(wi) >= x
轉換為:
- sum(vi - x * wi) >= 0
對 (vi - x * wi )的值進行排序貪心地進行選取。因此:
- C(x) = ((vi - x*wi) 從大到小排列中的前 k 個的和不小於0),即說明改 x 可以達到 k 個物品的 單位重量的價值
- 然後,就是用 二分搜索,來進行得到 最大滿足這一條件的 x
#include <iostream>
#include <functional>
#include <algorithm>
using namespace std;
/*
3
2
2 2 5 3 2 1
*/
const int maxn = 100000 + 100;
int n, k;
int w[maxn], v[maxn];
double ave[maxn];
//判斷是否滿足條件
bool C(double x)
{
for (int i = 0; i < n; i++)
{
ave[i] = v[i] - x * w[i];
}
//如果要自定義排序,裏面別寫成int了.....答案會出錯...
sort(ave, ave + n, greater<double>());
// for (int i = 0; i < n; i++) {
// cout << ave[i] << " ";
// }
// cout << endl;
//按從大到小取k個數求和
double sum = 0;
for (int i = 0; i < k; i++) {
sum += ave[i];
}
//觀察是否 可以取到 x為平均值
return sum >= 0;
}
void solve()
{
int INF = 0;
cin >> n >> k;
for (int i = 0; i < n; i++)
{
cin >> w[i] >> v[i];
INF += v[i];
}
double lh = 0, rh = INF, mid;
for (int i = 0; i < 100; i++)
{
mid = (lh + rh) / 2.0;
if (C(mid)) {
lh = mid;
}
else {
rh = mid;
}
}
printf("%.2f\n", lh);
}
int main()
{
solve();
return 0;
}
習題:POJ 2976 Dropping tests
來源:http://poj.org/problem?id=2976
題意:題目就是說,n個題目,你可以少做k個題目, 然後 n個題目分別為你得到的分數 ai, 和題目的分數 bi , 有個公式是:
, 要求就是:均值最大為多少,需要四舍五入。輸入多組數據,n, k同時為0時,終止輸入。和上面的例題屬於一模一樣的題目, 用二分解決最大化平均值的問題。
#include <iostream>
#include <iomanip>
#include <algorithm>
#include <functional>
#include <cstdio>
#include <cstdlib>
#include <cstring>
using namespace std;
const int maxn = 1000 + 10;
typedef long long LL;
int n, k;
LL a[maxn], b[maxn];
double INF;
double ave[maxn];
bool C(double x)
{
for (int i = 0; i < n; i++)
{
ave[i] = a[i] - x * b[i];
}
//降序
sort(ave, ave + n, greater<double>());
double sum = 0;
for (int i = 0; i < n - k; i++)
{
sum += ave[i];
}
return sum >= 0;
}
void solve()
{
double lh = 0, rh = INF, mid;
for (int i = 0; i < 100; i++)
{
mid = (lh + rh) / 2;
if (C(mid))
{
lh = mid;
}
else
{
rh = mid;
}
}
//和下面那種都可以
printf("%.f\n", lh * 100);
// cout << fixed << setprecision(0) << lh * 100 << endl;
}
int main()
{
while (cin >> n >> k && (n || k))
{
for (int i = 0; i < n; i++) {
cin >> a[i];
INF += a[i];
}
for (int i = 0; i < n; i++) {
cin >> b[i];
}
solve();
}
return 0;
}
二分算法的應用——最大化平均值