……

解题报告: AT1807

题目链接:AT1807 食塩水
其实是很裸的二分最大化平均值,这里讲一下思路。
很容易发现\(check\)函数很难写,这就是因为找不到用单调性或是什么性质来判断,那我们构造一下不就得了?
假设我们对于一个数\(x\)进行\(check\),
我们设所选的\(k\)个溶液构成的集合为\(S\),那显然只需满足

\[\dfrac{\sum\limits_{i\in S}p_i}{\sum\limits_{i\in S}w_i}\geqslant x \]

分数很别扭,乘过去,即得:

\[\sum\limits_{i\in S}p_i\geqslant \sum\limits_{i\in S}xw_i \]

移项,然后把求和合并起来:

\[\sum\limits_{i\in S}(p_i-xw_i)\geqslant 0 \]

维护下每个\(p_i-xw_i\),从大到小排序贪心取前\(k\)大的即可(或后\(k\)小的)。

其他的事:
注意这里的\(p_i\)是浓度,我们要把它处理成溶质的质量;
另外,最后的答案是要\(×100\),因为我们是要用百分数表示答案的。
时间复杂度显然是\(O(n\log n\log eps)\)(这里的\(eps\)指的是精度,\(\log eps\)即二分次数,自己估算一下即可,反正\(100\)准是没问题的。)

\(Code\)

#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm> 
#include<cstring>
using namespace std;
const int MAXN=1005;
const double EPS=1e-10;
int n,k;
double p[MAXN],w[MAXN];
double r=100.0,l=0.0,mid;
bool check(double x)
{
	double rt[MAXN],sum=0;
	memset(rt,0,sizeof(rt));//这里的清空是至关重要的,否则数组在函数中会任意赋值
	for(int i=1;i<=n;i++) rt[i]=p[i]-x*w[i];
	sort(rt+1,rt+n+1);
	for(int i=n;i>=n-k+1;i--) sum+=rt[i];//倒序就不用打cmp了
	return (sum-0)>EPS;
}
int main()
{
	scanf("%d%d",&n,&k);
	for(int i=1;i<=n;i++) scanf("%lf%lf",&w[i],&p[i]),p[i]*=w[i]/100;
   //处理溶质的质量,另外记得用double读入
	for(int i=1;i<=100;i++)//二分
	{
		mid=(l+r)/2;
		if(check(mid)) l=mid;
		else r=mid;
	}
	printf("%.9lf\n",mid*100);//转化为百分数
	return 0;
}
posted @ 2020-03-17 22:18  童话镇里的星河  阅读(163)  评论(0编辑  收藏  举报