K Best POJ - 3111(二分)

傳送門

題意:有n個物品的重量和價值分別是wi和vi。從中選出k個物品使得單位重量的價值最大。

題解:首先考慮二分做法

那麼一般最先想到的方法是把物品按照單位價值進行排序,從小到大貪心地進行選取。但是這個方法對於很多數據都有bug,所以是不行的。

實際上,對於這個問題使用二分搜索可以很好地解決,定義:

條件C(x):=可以選擇使得單位重量的價值不小於x

因此,原來的問題就變成了求滿足C(x)的最大的x。那麼怎麼判斷C(x)是否可行呢?假設我們選了某個物品的集合S,那麼它們的單位重量的價值是

\sum_{i\epsilon S}vi/ \sum_{i\epsilon S}wi

因此就變成了判斷是否存在S滿足下面的條件

\sum_{i\epsilon S} vi/\sum_{i\epsilon S}wi>=x

把這個不等式進行變形得到;

\sum_{i\epsilon S}(vi-x*wi)>=0

因此,可以對(vi-x*wi)的值進行排序貪心地進行選取。因此就變成了

C(x)=((vi-x*wi)從大到小排列中地前K個的和不小於0)

每次判斷的複雜度是O(nlogn).

附上代碼:(注意精度問題,以及二分玄學操作)

#include<iostream>
#include<cstdio>
#include<algorithm>

using namespace std;

const int maxn=1e5+50;
const int INF=1e7;

int n,k;
int v[maxn],w[maxn];

struct node{
    double val;
    int id;
};
node nodes[maxn];

bool cmp(node a,node b)
{
    return a.val>b.val;
}

int ok(double x)
{
    for(int i=0;i<n;i++){
        nodes[i].val=v[i]-x*w[i];
        nodes[i].id=i+1;
    }
    sort(nodes,nodes+n,cmp);
    double sum=0;
    for(int i=0;i<k;i++){
        sum+=nodes[i].val;
    }
    return sum>=0;
}

int main()
{
    scanf("%d%d",&n,&k);
    for(int i=0;i<n;i++){
        scanf("%d%d",&v[i],&w[i]);
    }
    double lb=-1,ub=INF;
    while(ub-lb>1e-8){
        double mid=(lb+ub)/2;
        if(ok(mid)){
            lb=mid;
        }else{
            ub=mid;
        }
    }
    for(int i=0;i<k;i++){
        printf("%d",nodes[i].id);
        if(i<k-1){
            printf(" ");
        }else{
            printf("\n");
        }
    }
    return 0;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章