POJ3111 K Best 【最佳牛圍欄】二分

K Best

Time Limit: 8000MS Memory Limit: 65536K
Total Submissions: 17073 Accepted: 4286
Case Time Limit: 2000MS Special Judge

Description
Demy has n jewels. Each of her jewels has some value vi and weight wi.
Since her husband John got broke after recent financial crises, Demy has decided to sell some jewels. She has decided that she would keep k best jewels for herself. She decided to keep such jewels that their specific value is as large as possible. That is, denote the specific value of some set of jewels S = {i1, i2, …, ik} as
在這裏插入圖片描述
Demy would like to select such k jewels that their specific value is maximal possible. Help her to do so.

Input
The first line of the input file contains n — the number of jewels Demy got, and k — the number of jewels she would like to keep (1 ≤ k ≤ n ≤ 100 000).
The following n lines contain two integer numbers each — vi and wi (0 ≤ vi ≤ 106, 1 ≤ wi ≤ 106, both the sum of all vi and the sum of all wi do not exceed 107).

Output
Output k numbers — the numbers of jewels Demy must keep. If there are several solutions, output any one.

Sample Input
3 2
1 1
1 2
1 3

Sample Output
1 2


一般我們想到這題應該是貪心,但是看到題目限制的時間八秒,數據也就1e5,貪心應該錯了吧(我第一次就是這樣寫的)。
其實這道題目是貪心和二分的經典應用。
sigma(vi) / sigma(wi) >= x {vi∈S, wi∈S}我假設這個成立,所以有
sigma(vi) - sigma(wi) * x >= 0 {vi∈S, wi∈S},每次去二分我們的x最後就可以得到我們的正確答案。

#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
const int maxn = 100010;
double v[maxn], w[maxn];
int n, m;
struct each {
    double sum;
    int id;
}select[maxn];
bool cmp(each a, each b) {
    return a.sum > b.sum;
}
bool judge(double x) {
    for(int i = 1; i <= n; i++) {
        select[i].sum = v[i] - w[i] * x;
        select[i].id = i;
    }
    sort(select + 1, select + n + 1,cmp);
    double sum = 0;
    for(int i = 1; i <= m; i ++)//只要取前m項大的就行,
        sum += select[i].sum;
    if(sum >= 0)    return true;
    return false;
}
int main() {
    while(scanf("%d %d", &n, &m) != EOF) {
        for(int i = 1; i <= n; i++)
            scanf("%lf %lf", &v[i], &w[i]);
        double l = 0, r = 1000000;
        while(r - l >= 1e-7) {
            double mid = (l + r) / 2;
            if(judge(mid)) l = mid;
            else    r = mid;
        }
        cout << select[1].id;
        for(int i = 2; i <= m; i++)//最後一次的最佳開始輸出對應的id
            cout << " " << select[i].id;
        cout << endl;
    }
    return 0;
}



最佳牛圍欄

農夫約翰的農場由 N 塊田地組成,每塊地裏都有一定數量的牛,其數量不會少於1頭,也不會超過2000頭。
約翰希望用圍欄將一部分連續的田地圍起來,並使得圍起來的區域內每塊地包含的牛的數量的平均值達到最大。
圍起區域內至少需要包含 F 塊地,其中 F 會在輸入中給出。
在給定條件下,計算圍起區域內每塊地包含的牛的數量的平均值可能的最大值是多少。

輸入格式
第一行輸入整數 N 和 F ,數據間用空格隔開。
接下來 N 行,每行輸出一個整數,第i+1行輸出的整數代表,第i片區域內包含的牛的數目。

輸出格式
輸出一個整數,表示平均值的最大值乘以1000再 向下取整 之後得到的結果。

數據範圍
1≤N≤100000
1≤F≤N
輸入樣例:
10 6
6
4
2
10
3
8
5
9
4
1
輸出樣例:
6500


按照題意,我們只要找到一個最大區間滿足條件就行。

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn = 100010;
int n, m;
double a[maxn], sum[maxn];

bool judge(double x) {
    for(int i = 1; i <= n; i++) sum[i] = sum[i - 1] + a[i] - x;//通過減去一個當前查找的值,減小數的值,其實可有可無。
    double minn = 0;
    for(int i = 0, j = m; j<= n; j++, i++) {
        minn = min(minn, sum[i]);//找到前面的最小值。
        if(sum[j] >= minn)  return true;//如果有一個區間滿足立刻return true
    }
    return false;
}
int main() {
    scanf("%d %d", &n, &m);
    for(int i = 1; i <= n; i++) scanf("%lf", &a[i]);
    double l = 0, r = 2000;
    while(r - l >= 1e-5) {
        double mid = (l + r) / 2;
        if(judge(mid))  l = mid;
        else    r = mid;
    }
    printf("%d\n",int(r * 1000));//按照題意輸出。
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章