POJ 3111 K Best

二分,排序,贪心。

最优比率生成树,可以二分$+$贪心来实现,不过这样做精度不行。

如果是这样一个问题,该如何解决:问你$n$个里面选择$k$个,能否使得$\frac{{\sum\limits_{j = 1}^k {{v_{{i_j}}}} }}{{\sum\limits_{j = 1}^k {{w_{{i_j}}}} }} ≥ x$。

上述问题等价于问你:$n$个里面选择$k$个,能否使得$\sum\limits_{j = 1}^k {({v_{{i_j}}} - x×{w_{{i_j}}})}  ≥ 0$。

也就是说,我们需要令${f_i} = {v_i} - x×{w_i}$,按照${f_i}$从大到小排序,选择前$k$个计算和$sum$。

如果$sum≥0$,也就是说$\frac{{\sum\limits_{j = 1}^k {{v_{{i_j}}}} }}{{\sum\limits_{j = 1}^k {{w_{{i_j}}}} }} ≥ x$成立;否则不成立。

因为这个问题是遵循单调性的,$x$越大可能性越小,因此只要二分$x$,然后验证就可以了。时间复杂度$O(50*n*\log n)$。

特别要注意的是精度问题:

$[1].$计算$sum$的时候,最后要加上一个$eps$,我在这卡了很久精度。

$[2].$二分的话差不多$50$次就可以了,$100$次$TLE$了,也没有必要进行$100$次,因为实际上是只要$\log {10^7}$次。

#pragma comment(linker, "/STACK:1024000000,1024000000")
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<vector>
#include<map>
#include<set>
#include<queue>
#include<stack>
#include<iostream>
using namespace std;
typedef long long LL;
const double pi=acos(-1.0),eps=1e-6;
void File()
{
    freopen("D:\\in.txt","r",stdin);
    freopen("D:\\out.txt","w",stdout);
}
template <class T>
inline void read(T &x)
{
    char c = getchar(); x = 0;while(!isdigit(c)) c = getchar();
    while(isdigit(c)) { x = x * 10 + c - '0'; c = getchar();  }
}

const int maxn=100010;
struct X { int v,w;}s[maxn];
int n,k,ans[maxn];
struct XX {double num; int id;}t[maxn];
double Max;

bool cmp(XX a,XX b){ return a.num>b.num; }

bool check(double x)
{
    for(int i=1;i<=n;i++)
    {
        t[i].num=1.0*s[i].v-x*s[i].w;
        t[i].id=i;
    }
    sort(t+1,t+1+n,cmp);

    double sum=0;
    for(int i=1;i<=k;i++) sum=sum+t[i].num;
    
    if(sum+eps>=0) 
    {
        for(int i=1;i<=k;i++) ans[i]=t[i].id;
        return 1;
    }
    return 0;
}

int main()
{
    while(~scanf("%d%d",&n,&k))
    {
        for(int i=1;i<=n;i++)
            scanf("%d%d",&s[i].v,&s[i].w);

        double L=0.0,R=10000000.0;
        int t=50;
        while(t--)
        {
            double mid=(L+R)/2;
            if(check(mid)) L=mid;
            else R=mid;
        }

        for(int i=1;i<=k;i++)
        {
            printf("%d",ans[i]);
            if(i<k) printf(" "); else printf("\n");
        }
    }
    return 0;
}

 

posted @ 2016-09-01 22:23  Fighting_Heart  阅读(179)  评论(0编辑  收藏  举报