洛谷 P1570 - KC喝咖啡 - 二分答案
洛谷 P1570 - KC喝咖啡 - 二分答案
二分答案简介
二分答案是对于二分查找思想的一种应用。通过对答案出现的区间进行二分查找来判定可能的答案。二分答案一般用在以下情形的题目:
- 直接求解答案十分复杂,或需要通过指数级别的枚举来完成。
- 当以答案值ans作为函数自变量时,存在这样的一个单调函数可以作为区间折半的判定函数。
一般而言,使用二分答案解题通常需要以下几步:
- 确定答案出现的区间范围
- 找到单调函数(最关键步骤)
- 二分答案,通过单调缩小答案区间。
本题题解
依题意,需要找到最大的答案\(x\),使得\(x = \frac{\sum v_i}{\sum c_i}\)。用搜索,复杂度为\(2^n\),01背包也行不通,本题的\(x\)表达式不满足dp的无后效性。而题目给出 \(x \in [0,1000]\),考虑二分答案。注意本题\(x\)是小数形式,因此本题的大小判定需要针对浮点数重写比较函数或另外定义EPS。
如何二分呢?注意到\(x = \frac{\sum v_i}{\sum c_i}\),不妨设\(y(x) = \sum v_i - x * \sum c_i = \sum (v_i - x*c_i)\) 。当进行二分的时候,正在判断\(mid\)的值是否为最大的\(x\),故需要检验 \(y(mid)_{max}\)与0的大小关系
-
\(y(mid)_{max} >= 0\)
这说明存在这样的一组方案使得\(x = \frac{\sum v_i}{\sum c_i} >= mid\)。因此有\(x \in [mid,r]\)
-
\(y(mid)_{max} < 0\)
这说明不存在这样的一组方案使得\(x = \frac{\sum v_i}{\sum c_i} >= mid\),即所有的\(\frac{\sum v_i}{\sum c_i}\)都小于\(mid\)。因此有\(x \in [l,mid]\)
另外,\(y(mid)_{max}\)很好求,只需要分别计算\(v_i - mid * c_i\)后排序去后m个即可。
答案
#include <cstdio>
#include <cmath>
#include <vector>
#define MaxN 200+5
#define EPS 1e-6
using namespace std;
int v[MaxN]; // 美味度
int c[MaxN]; // 时间
bool bigthan(double a,double b){
return a >= b + EPS;
}
bool equal(double a,double b){
return fabs(a-b) < EPS;
}
double bs(int n,int m){
double l = 0,r = 1000;
while(bigthan(r,l)){
double mid = (r + l) / 2;
vector<double> vec;
for(int i = 1; i <= n; i++){
vec.push_back(v[i] - c[i]*mid);
}
sort(vec.begin(),vec.end());
double sum = 0;
for(int i = n-1; i >= n-m; i--){
sum += vec[i];
}
if(bigthan(sum,0)){
l = mid;
}else{
r = mid;
}
}
return r;
}
int main(){
int n,m;
scanf("%d %d",&n,&m);
for(int i = 1; i <= n; i++){
scanf("%d",&v[i]);
}
for(int i = 1; i <= n; i++){
scanf("%d",&c[i]);
}
printf("%.3f",bs(n,m));
return 0;
}
---- suffer now and live the rest of your life as a champion ----