1. 程式人生 > >二分算法的應用——最大化平均值

二分算法的應用——最大化平均值

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;
}

二分算法的應用——最大化平均值