AtCoder 1807 食塩水

题意

\(n\) 瓶食盐水,第 \(i\) 瓶为质量 \(w_i\),浓度 \(p_i\%\) 的食盐水,需要选出 \(k\) 瓶食盐水混合在一起,问最大浓度。

\(\texttt{Data Range:}1\leq n,k\leq 1000\)

题解

二分答案。

一开始在想各种贪心,然后各种贪心都假了,但是注意到这个东西是有单调性,(如果存在一种方案混合出来的食盐水浓度大于 \(p\) 那么也一定存在一种方案的浓度大于比 \(p\) 小的值),所以可以二分答案变成判定性问题。

现在考虑能不能选出 \(k\) 瓶食盐水混合起来浓度大于某个二分到的 \(p\),也即:

\[\frac{\sum\limits_{i=1}^{k}w_ip_i}{\sum\limits_{i=1}^{k}w_i}\geq p \]

乘过去再移过来

\[\sum\limits_{i=1}^{k}w_i(p_i-p)\geq 0 \]

所以说我们只需要对 \(w_i(p_i-p)\) 排序然后贪心选最大的 \(k\) 个即可,实现可能有一些细节需要注意。

代码

#include<bits/stdc++.h>
using namespace std;
typedef int ll;
typedef long long int li;
typedef long double db;
const ll MAXN=2e5+51;
const db eps=1e-10;
struct Node{
    ll p,w;
};
Node x[MAXN];
ll n,kk;
db l,r,mid;
db c[MAXN];
inline ll read()
{
    register ll num=0,neg=1;
    register char ch=getchar();
    while(!isdigit(ch)&&ch!='-')
    {
        ch=getchar();
    }
    if(ch=='-')
    {
        neg=-1;
        ch=getchar();
    }
    while(isdigit(ch))
    {
        num=(num<<3)+(num<<1)+(ch-'0');
        ch=getchar();
    }
    return num*neg;
}
inline ll check(db mid)
{
    db c2=0;
    for(register int i=1;i<=n;i++)
    {
        c[i]=(x[i].p-mid)*x[i].w;
    }
    sort(c+1,c+n+1,greater<db>());
    for(register int i=1;i<=kk;i++)
    {
        c2+=c[i];
    }
    return c2>=0;
}
int main()
{
    n=read(),kk=read(),l=0,r=100;
    for(register int i=1;i<=n;i++)
    {
        x[i].w=read(),x[i].p=read();
    }
    while(r-l>=eps)
    {
        mid=(l+r)/2;
        check(mid)?l=mid:r=mid;
    }
    printf("%.9Lf\n",l);
}
posted @ 2020-10-26 11:10  Karry5307  阅读(234)  评论(0编辑  收藏  举报