P1570 KC 喝咖啡 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
让求
∑
v
i
∑
c
i
\frac { \sum{ v_i}} { \sum{c_i}}
∑ci∑vi的最大值,假设值为
a
n
s
ans
ans,则
a
n
s
=
∑
v
i
∑
c
i
ans = \frac {\sum{v_i}} {\sum{c_i}}
ans=∑ci∑vi
进行移项做恒等变换,得:
a
n
s
×
∑
c
i
−
∑
v
i
=
0
ans \times \sum{c_i} - \sum{v_i} = 0
ans×∑ci−∑vi=0
可以发现,最优解一定是尽可能让这个式子为0。
此时,就找到二段性。
令 v a l = a n s × ∑ c i − ∑ v i val = ans \times \sum{c_i} - \sum{v_i} val=ans×∑ci−∑vi。
- 若 v a l ≤ 0 val \le 0 val≤0: a n s ans ans更大可能更优
- 若 v a l > 0 val \gt 0 val>0: a n s ans ans更小可能更优
因此套用最小值最大的二分模板即可。
如何快速的找到 m m m个调料进行融合:根据二分的 a n s ans ans值对第调料按 a n s × c − v ans \times c - v ans×c−v进行排序即可。
代码如下:
#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 + 21;
struct no {
int v,c;
double w;
bool operator<(const no & rhs) {
return w < rhs.w;
}
}a[N];
int main()
{
int n,m; cin>>n>>m;
for(int i = 1; i <= n; ++i) cin>>a[i].v;
for(int i = 1; i <= n; ++i) cin>>a[i].c;
double l = 0, r = 1e9;
auto check = [&](double mid) -> bool {
for(int i = 1; i <= n; ++i) a[i].w = a[i].c * 1.0 * mid - a[i].v;
sort(a + 1, a + n + 1);
double sum = 0;
for(int i = 1; i <= m; ++i) sum += a[i].w;
return sum <= 0;
};
while(l < r - 1e-6) {
double mid = (l + r) / 2;
if(check(mid)) l = mid;
else r = mid;
}
printf("%.3lf",l);
}