K Best POJ - 3111(二分)
阿新 • • 發佈:2018-10-31
題意:有n個物品的重量和價值分別是wi和vi。從中選出k個物品使得單位重量的價值最大。
題解:首先考慮二分做法
那麼一般最先想到的方法是把物品按照單位價值進行排序,從小到大貪心地進行選取。但是這個方法對於很多資料都有bug,所以是不行的。
實際上,對於這個問題使用二分搜尋可以很好地解決,定義:
條件C(x):=可以選擇使得單位重量的價值不小於x
因此,原來的問題就變成了求滿足C(x)的最大的x。那麼怎麼判斷C(x)是否可行呢?假設我們選了某個物品的集合S,那麼它們的單位重量的價值是
因此就變成了判斷是否存在S滿足下面的條件
把這個不等式進行變形得到;
因此,可以對(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; }