刷题记 - XXVI (二分查找)
Posted HouZAJ的堆栈
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了刷题记 - XXVI (二分查找)相关的知识,希望对你有一定的参考价值。
(QQ音乐没有对应曲目)
前言
首先很抱歉因为考完高数还有一堆事儿,再加上我现在正在啃的东西比较难,所以拿之前做过的一个系统性的东西来凑 T^T
"二分查找是一种思想",这是冬训时师兄的话。个人觉得作为ACMer更重要的是,学了算法和数据结构,能够运用,比如最近张哥发的文章中,有网友提出用二分查找找出窃取照片的人,在个人看来这就是运用啦~哪天个人也能这么聪明就好了 QAQ
Solve It - UVA - 10341
题意
给定p、q、r、s、t,求解给定方程的根
思路
首先要判断单调性,对方程求导,得 -pe^(-x) + qcosx - rsinx + s(secx)^2 + 2xt,把已知条件代入,可得这条式子是恒小于或等于0的,故原方程单调递减,再二分求解即可
#include <iostream>
#include <cstdio>
#include <cmath>
using namespace std;
const double eps = 1e-7;
double p, q, r, s, t, u;
double fun(double x) { return p*exp(-x) + q*sin(x) + r*cos(x) + s*tan(x) + t*x*x + u; }
int main(){
while(~scanf("%lf%lf%lf%lf%lf%lf", &p, &q, &r, &s, &t, &u)){
double l = 0, r = 1;
while(fun(l) * fun(r) <= 0 && fabs(fun(l)) > eps){
double mid = (l + r)/2;
if(fun(mid) > 0){
l = mid;
}else{
r = mid;
}
}
if(fabs(fun(l)) < eps){
printf("%.4f ", l);
}else{
printf("No solution ");
}
}
}
Pie - POJ 3122
题意
有很多个半径为R1,R2,……,Rn的批萨,现在要分同样大小的批萨(可以不同形状)给M个人,问最大大小能是多少
思路
稍微有点隐晦的二分
对所求的最大大小进行二分,分的人数 >= M 就缩左边界,否则缩右边界
#include <iostream>
#include <cstdio>
#include <cmath>
using namespace std;
const double eps = 1e-7;
const double PI = acos(-1);
const int N = 1e4 + 15;
double rr[N];
int main(){
int t;
scanf("%d", &t);
while(t--){
int n, m;
scanf("%d%d", &n, &m);
m++;
for(int i = 0; i < n; i++){
scanf("%lf", &rr[i]);
}
double l = PI/10005.0, r = 10000.0*10000.0*PI;
while(r - l > eps){
int sum = 0;
double mid = (l + r)/2.0;
for(int i = 0; i < n; i++){
sum += (int)(rr[i] * rr[i] * PI/mid);
}
if(sum >= m){
l = mid;
}else{
r = mid;
}
}
printf("%.4f ", l);
}
}
Trick or Treat - ZOJ 3386
题意
给定N个不同的点,求在x轴上的一点使得所有点到它的距离中的最大值最小
思路
函数叠加,但仍然先递减后递增,故用三分查找的方法找出最小值
#include <iostream>
#include <cstdio>
#include <cmath>
#include <climits>
using namespace std;
const double eps = 1e-5;
const int N = 50005;
double coordinate[N][2] = {0};
double calc(double x1, double y1, double x2, double y2) { return (x1 - x2)*(x1 - x2) + (y1 - y2)*(y1 - y2); }
double f(double x, int n){
double ans = calc(coordinate[0][0], coordinate[0][1], x, 0.0);
for(int i = 1; i < n; i++){
ans = max(ans, calc(coordinate[i][0], coordinate[i][1], x, 0.0));
}
return ans;
}
int main(){
int n;
while((~scanf("%d", &n)) && n){
for(int i = 0; i < n; i++){
scanf("%lf%lf", &coordinate[i][0], &coordinate[i][1]);
}
double l = -200000.0;
double r = 200000.0;
while(r - l > eps){
double lm = l + (r - l)/3.0;
double lr = r - (r - l)/3.0;
if(f(lm, n) < f(lr, n)){
r = lr;
}else{
l = lm;
}
}
printf("%.9f %.9f ", l, sqrt(f(l, n)));
}
}
Trick or Treat - ZOJ 3386
题意
给定N个点,对于每个点给出它的x, y, vx, vy,问这些点何时能取得最大距离的最小值
思路
首先写一下任意两点的距离公式 d^2 = [(xi + t*vxi) - (xj + t*vxj)]^2 + [(yi + t*vyi) - (yj + t*vyj)]^2,该公式可化简,得到二次函数表达式,先递减再递增
既然是二次函数表达式,那么直接叠加,仍然会先递减,后递增,因此用三分查找的方法找出最小值
#include<iostream>
#include<string>
#include<algorithm>
#include <cstdio>
#include <cmath>
using namespace std;
const int N = 300 + 15;
const double eps = 1e-5;
double x[N], y[N], vx[N], vy[N];
double a[N*N], b[N*N], c[N*N];
inline double twice(double x) {return x*x;}
double getMax(double t, int n){
double ans = 0;
for(int i = 0; i < n; i++){
for(int j = i + 1; j < n; j++){
ans = max(ans, sqrt(twice(x[i] + vx[i]*t - x[j] - vx[j]*t) + twice(y[i] + vy[i]*t - y[j] - vy[j]*t)));
}
}
return ans;
}
int main(){
int t;
int caseno = 1;
scanf("%d", &t);
while(t--){
int n;
scanf("%d", &n);
for(int i = 0; i < n; i++){
scanf("%lf%lf%lf%lf", &x[i], &y[i], &vx[i], &vy[i]);
}
double ans = 0;
double l = 0, r = 1e6;
while(r - l > eps){
double lm = (r - l)/3 + l;
double rm = r - (r - l)/3;
double lval = getMax(lm, n);
double rval = getMax(rm, n);
if(lval < rval){
ans = lval;
r = rm;
}else{
ans = rval;
l = lm;
}
}
printf("Case #%d: %.2f %.2f ", caseno++, l, ans);
}
}
以上是关于刷题记 - XXVI (二分查找)的主要内容,如果未能解决你的问题,请参考以下文章