DAY2:二分与三分

\[Designed\ By\ FrankWkd\ -\ Luogu@Lwj54joy,uid=845400
\]

特别感谢 此次课的主讲 - Kwling

二分概述

  • 二分法,在一个单调有序的集合或函数中查找一个解,每次分为左右两部分,判断解在那个部分并调整上下界,直到找到目标元素,每次二分都将舍弃一般的查找空间,因此效率很高。

二分常见模型

  • 二分答案

    最小值最大(或是最大值最小)问题,这类双最值问题常常选用二分法求解,也就是确定答案后,配合贪心,\(DP\) 等其他算法检验这个答案是否合理,将最优化问题转化为判定性问题。例如,将长度为 \(n\) 的序列 \(a_i\) 分为最多 \(m\) 个连续段,求所有分法中每段和的最大值的最小是多少?

  • 二分查找

    用具有单调性的布尔表达式求解分界点,比如在有序数列中求数字 \(x\) 的排名。

  • 代替三分(?存疑)

    有时,对于一些单峰函数,我们可以用二分导函数的方法求解函数的极值,这时通常将函数的定义域定义为整数域求解比较方便,此时 \(d_x\) 可以直接去整数 \(1\) 。

二分使用范围:

  • 必须具备单调性或者是二段性

参考leetcode暑假打卡活动2019——week1中

视频链接:传送门

写二分的过程:

  1、确定二分边界

  2、编写二分的代码框架

  3、设计一个check(性质)

  4、判断一下区间如何更新

  5、如果更新方式是 L = Mid , R = Mid - 1 ,那么在算mid时要加1

二分查找

  • 手写二分【模版】

    • 记得将无序数组变得有序!
    • 记得考虑无解的情况。
    • #include <bits/stdc++.h>
      using namespace std;
      int target,n;
      int a[1010001];
      int main(Designer = 洛谷@Lwj54joy,uid=845400){
      cin>>n>>target;
      for(int i = 1;i <= n;i++){
      cin>>a[i];
      }
      int l = 1,r = n;
      while(l <= r){
      int mid = l+r>>1;
      if(a[mid] < target) l = mid+1;
      else r = mid-1;
      }
      if(a[l] != target) cout<<-1<<endl;
      else cout<<l<<endl; }
  • 函数二分【模版】
    • 直接用lower_bound求左边界,超级短。
    • #include <bits/stdc++.h>
      using namespace std; int n,target;
      int a[1010001];
      int main(Designer = 洛谷@Lwj54joy,uid=845400){
      cin>>n>>target;
      for(int i = 1;i <= n;i++) cin>>a[i];
      if(binary_search(a+1,a+1+n,target) == true){
      int answer = lower_bound(a+1,a+1+n,target)-a;
      cout<<answer<<endl;
      } }

二分题目解法&思路

\(T1\)

#include <bits/stdc++.h>
using namespace std; int n;
int target,times;
int a[1010001];
int main(Designer = 洛谷@Lwj54joy,uid=845400){
scanf("%d%d",&n,&times);
for(int i = 1;i <= n;i++){
scanf("%d",&a[i]);
}
while(times--){
scanf("%d",&target);
int l = 1,r = n;
while(l <= r){
int mid = l+r>>1;
if(a[mid] < target) l = mid+1;
else r = mid-1;
}
if(a[l] != target) printf("-1 ");
else printf("%d ",l);
} }

\(T2\)

#include <bits/stdc++.h>
using namespace std; int n,l = INT_MIN,r,m;
int a[1010001]; bool check(int mid){
int tot = 0;
int times = 1;
for(int i = 1;i <= n;i++){
if(tot+a[i] <= mid) tot += a[i];
else tot = a[i],times++;
}
return times <= m;
} int main(Designer = 洛谷@Lwj54joy,uid=845400){
cin>>n>>m;
for(int i = 1;i <= n;i++){
cin>>a[i];
l = max(l,a[i]);
r += a[i];
}
while(l <= r){
int mid = l+r>>1;
if(check(mid)) r = mid-1;
else l = mid+1;
}
cout<<l<<endl; }

\(T3\)

	#include <bits/stdc++.h>
using namespace std;
int L,n,m;
int a[2101010];
int ans;
inline bool check(int mid){
int times = 0,ip = 0;
for(int i = 1;i <= n+1;i++){
if(a[i]-a[ip] < mid){
times++;
if(times > m) return false;
}else ip = i;
}
return times <= m;
}
int main(Designer = 洛谷@Lwj54joy,uid=845400){
cin>>L>>n>>m;
a[n+1] = L;
int l = 1,r = L;
for(int i = 1;i <= n;i++){
cin>>a[i];
l = min(l,a[i]-a[i-1]);
}
l = min(l,L-a[n]);
while(l <= r){
int mid = l+r>>1;
if(check(mid)){
l = mid+1;
}else r = mid-1;
}
cout<<l-1<<endl; }

\(T4\)

#include <bits/stdc++.h>
using namespace std;
int n,m;
int a[2101010];
int ans;
inline bool check(int mid){
int dis = a[1],mm = 1;
for(int i = 2;i <= n;i++){
if(a[i]-dis >= mid){
mm++;
dis = a[i];
}
}
return mm >= m;
}
int main(Designer = 洛谷@Lwj54joy,uid=845400){
cin>>n>>m;
int l = 1,r = -99;
for(int i = 1;i <= n;i++){
cin>>a[i];
}
sort(a+1,a+1+n);
for(int i = 1;i <= n;i++){
l = min(l,a[i]-a[i-1]);
}
r = a[n]-a[1];
while(l <= r){
int mid = l+r>>1;
if(check(mid)){
l = mid+1;
}else r = mid-1;
}
cout<<l-1<<endl; }

\(T5\)

#include <bits/stdc++.h>
using namespace std;
int a[101010],n,L;
double b[101010];
int main(Designer = 洛谷@Lwj54joy,uid=845400){
cin>>n;
cin>>L;
int maxx = 0;
for(int i = 1;i <= n;i++) cin>>a[i],maxx = max(maxx,a[i]);
double l = 0,r = maxx*1.0,mid;
while(r-l > 1e-5){
mid = (l+r)/2;
for(int i = 1;i <= n;i++)
b[i] = (b[i-1]+a[i]*1.0-mid)*1.0;
double ans = 0;
bool fl = 0;
for(int i = L;i <= n;i++){
ans = min(ans,b[i-L]);
if(b[i]-ans >= 0.0){
fl = 1;
break;
}
}
if(fl == 1) l = mid;
else r = mid;
}
cout<<(int)(r*1000)<<endl;
}

\(T6\)

  • AccodersP2047 |【一本通提高篇二分与三分】扩散

  • 洛谷P1661 | 扩散

    思路:

    一想到大数据的联通块,绝对是并查集。

    那么,题中联通的定义也就转化成了:这些点具有相同的公共祖先。即:

    \(F[find(i)] == k\)

    其中, \(k\) 为常量, \(i\) 为 \(1-n\) 中的任意值。

    那么,仔细想想就能想得到,直接二分扩散时间。因为每个点都在扩散,那么两点之间的曼哈顿距离必须小于等于扩散时间乘 \(2\) 才能保证两点在扩散中相遇。

    在 \(check\) 函数中,我们直接暴力枚举每个点,如果在 \(mid\) 时间及以前能够相遇,就在并查集中合并二者。最后,如果所有的点拥有相同的公共祖先,即 \(F[find(i)] == k\) ,就可以返回 \(true\) 了。

#include <bits/stdc++.h>
using namespace std;
struct node{
int x,y;
}a[1010101];
int f[1010101][2];
int n; int find(int x){
if(f[x][0] != x) return f[x][0] = find(f[x][0]);
return x;
} void merge(int x,int y){
f[find(x)][0] = f[find(y)][0];
f[find(y)][1] += f[find(x)][1];
} bool check(int x){
for(int i = 1;i <= n;i++){
for(int j = i+1;j <= n;j++){
if(abs(a[i].x-a[j].x)+abs(a[i].y-a[j].y) <= x*2 and find(i) != find(j)) merge(i,j);
}
}
int k = find(1);
for(int i = 2;i <= n;i++){
if(find(i) != k) return false;
}
return true;
} int main(Designer = 洛谷@Lwj54joy,uid=845400){
cin>>n;
for(int i = 1;i <= n;i++){
cin>>a[i].x>>a[i].y;
}
int l = 0,r = 1e9;
while(l <= r){
for(int i = 1;i <= n;i++) f[i][0] = i,f[i][1] = 1;
int mid = l+r>>1;
if(check(mid)) r = mid-1;
else l = mid+1;
}
cout<<l<<endl;
}

三分

  • 三分算法(Ternary Search Algorithm)是一种用于在单峰函数中寻找极值的优化算法。它通过将搜索区间分为三个部分并比较函数在两个划分点的取值,逐步缩小搜索范围来确定极值的位置。(求解带曲线的多次函数)

  • 三分算法的基本思路如下:

    1. 初始化搜索区间,通常是整个定义域的区间。

    2. 将搜索区间分为三个部分,通常采用两个划分点将整个区间分成三等份。

    3. 计算两个划分点的函数取值。

    4. 比较两个划分点的函数取值,如果其中一个划分点的函数值更大(或更小),则极值在该划分点的同侧。

    5. 根据比较结果更新搜索区间,将较小(或较大)的划分点所在的区间作为新的搜索区间。

    6. 重复步骤3到步骤5,直到搜索区间足够小,或达到一定的迭代次数。

    7. 返回搜索区间的中点作为极值的近似位置。

三分算法的时间复杂度为 \(O(logN)\) ,其中 \(N\) 为搜索区间的长度。它的优势在于可以在单峰函数中快速找到极值的位置,但不适用于非单调函数或多峰函数的寻优问题。

三分题目解法&思路

\(T1\)

  • 洛谷P1883 |【模板】三分 | 函数

    思路:

    观察题目图像得 \(F(x)\) 函数是一个下凸函数。即对于任意的 \(x1<x2<x3\) ,

    不存在 \(F(x1)<F(x2)>F(x3)\) ,根据这个定义,我们可以通过三分法求该函数的最值。

    随便画出二次函数的一段(二次函数是一个特殊的下凸函数),

    我们发现一个下凸函数其上四个点无非有以下几种情况:

    设下凸函数上四个点横坐标分别为 \(x1<x2<x3<x4\) ,

    并且我们已经确定该函数的最小值在 \([x1,x4]\) 之间

    若F(x1)<F(x2),则函数的最小值的x的取值范围一定在[x1,x2]之间,因为根据定义,

    若有 \(F(x1)<F(x2)\) ,必有 \(F(x1)<F(x2)<F(x3)<F(x4)\) ;

    若 \(F(x3)>F(x4)\) ,同理可得,函数最小值的取值范围一定是在 \([x3,x4]\) 之间。

    若 \(F(x1)>F(x2)\ \&\ F(x2)<F(x3)\) ,

    则函数的最小值的 \(x\) 的取值范围一定在 \([x1,x3]\) 之间,

    因为下凸函数有且仅有一个低谷,并且下凸函数的最小值在低谷中。

    若 \(F(x4)>F(x3)\ \&\ F(x3)<F(x2)\),

    同理,函数最小值的取值范围一定在 \([x2,x4]\) 之间。

    最后就只剩下一种情况了,此时函数最小值的取值范围一定在 \([x2,x3]\) 之间。

#include <bits/stdc++.h>
using namespace std;
int n;
double a[101010],b[101010],c[101010];
double f(double x){
double mx = -1e9;
for(int i = 1;i <= n;i++) mx = max(mx,a[i]*x*x+b[i]*x+c[i]);
return mx;
}
int main(Designer = 洛谷@Lwj54joy,uid=845400){
int T;
cin>>T;
while(T--){
scanf("%d",&n);
for(int i = 1;i <= n;i++) scanf("%lf%lf%lf",&a[i],&b[i],&c[i]);
double l = 0,r = 1000;
while(r-l>1e-11){
double m1 = l+(r-l)/3,m2 = r-(r-l)/3;
if(f(m1) <= f(m2)) r = m2;
else l = m1;
}
printf("%.4lf\n",f(r));
}
}

\(T2\)

  • AccodersP2045 |【一本通提高篇二分与三分】曲线

    思路: 由于函数 \(S\) 是开口向上的二次函数(当 \(a=0\) 时,是一次函数),由 \(S\) 的定义可知,\(S\) 或者是一个先单调减、后单调增的下凸函数,或者是一个单调函数,\(F(x)=max(S_x)\) 也满足单调性。选用三分法很容易求得某个区间内的最小值。
#include <bits/stdc++.h>
using namespace std;
int n;
double a[101010],b[101010],c[101010];
double f(double x){
double mx = -1e9;
for(int i = 1;i <= n;i++) mx = max(mx,a[i]*x*x+b[i]*x+c[i]);
return mx;
}
int main(Designer = 洛谷@Lwj54joy,uid=845400){
int T;
cin>>T;
while(T--){
scanf("%d",&n);
for(int i = 1;i <= n;i++) scanf("%lf%lf%lf",&a[i],&b[i],&c[i]);
double l = 0,r = 1000;
while(r-l>1e-11){
double m1 = l+(r-l)/3,m2 = r-(r-l)/3;
if(f(m1) <= f(m2)) r = m2;
else l = m1;
}
printf("%.4lf\n",f(r));
}
}

\(T3\)

  • AccodersP2048 |【一本通提高篇二分与三分】灯泡
  • 洛谷P5931 | [清华集训2015]灯泡

    思路:

    首先可以分两种情况:

    (1)有影子在墙上

    (2)没有影子在墙上

    没有影子在墙上的时候,通过计算可以得出当光线照在墙角的时候最大。

    设人到墙的距离为 \(x\)

    这个时候我们可以得到 \(x\) 的上界 \(h * D / H\) (相似)

    这个时候就可以合并到第一种情况。

    第一种情况可以推出影子长度 \(L = x + (D * h - x * H) / (D - x)\)

    不需要化简,只要在程序中可以算出就行了。

    有某个 \(x\) 是最大的。

    果然,有最值。
	#include <bits/stdc++.h>
using namespace std;
int t;
double hb,h,d;
double f(double x){
double l = hb-(d*(hb-h)/x);
return d-x+l;
}
int main(Designer = 洛谷@Lwj54joy,uid=845400){
cin>>t;
while(t--){
cin>>hb>>h>>d;
double l = d-(h*d/hb),r = d;
while(r-l >= 1e-5){
double m1 = l+(r-l)/3,m2 = r-(r-l)/3;
if(f(m1) < f(m2)) l = m1;
else r = m2;
}
printf("%.3lf\n",f(r));
}
}

\(\huge Thanks.\)



感谢阅读,若有问题或错误,欢迎评论(或私信我)。

2025 Designed By @洛谷Lwj54joy,uid=845400

2025dsfz集训Day2:二分与三分的更多相关文章

  1. HZNU-ACM寒假集训Day2小结 二分答案

    Day2 ---二分 这里直接给出模板 两种对应不同的情况 可以借助数轴理解 int bsearch_1(int l, int r) { while (l < r) { ; if (check( ...

  2. YBT 1.2 二分与三分

    二分与三分: 二分(单调性),三分(单峰性),这章主要考数学,模拟,与读题!!! 链接: https://pan.baidu.com/s/1AJTl_0p5Lh4T1MmNwyJWzg 密码: j3j ...

  3. WC集训DAY2笔记 组合计数 part.1

    目录 WC集训DAY2笔记 组合计数 part.1 基础知识 组合恒等式 错排数 卡特兰数 斯特林数 伯努利数 贝尔数 调和级数 后记 补完了几天前写的东西 WC集训DAY2笔记 组合计数 part. ...

  4. 2019暑期金华集训 Day2 线性代数

    自闭集训 Day2 线性代数 高斯消元 做实数时,需要找绝对值最大的作为主元,以获取更高精度. 在欧几里得环(简单例子是模合数)意义下也是对的.比如模合数意义下可以使用辗转相除法消元. 欧几里得环:对 ...

  5. 暑假集训Day2 互不侵犯(状压dp)

    这又是个状压dp (大型自闭现场) 题目大意: 在N*N的棋盘里面放K个国王,使他们互不攻击,共有多少种摆放方案.国王能攻击到它上下左右,以及左上左下右上右下八个方向上附近的各一个格子,共8个格子. ...

  6. ACM : HDU 2899 Strange fuction 解题报告 -二分、三分

    Strange fuction Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Tot ...

  7. Chapter2 二分与三分

    T1 给一个N个数的序列,分成M段,每段最大值最小 sol:二分最大值,贪心Check T2 平面上n个点,每个点每s会向周围扩散一个单位长度,两个点联通当且仅当扩散有交点,问什么时候这n个点联通 s ...

  8. codeforces#403—B题(二分,三分)

    B. The Meeting Place Cannot Be Changed time limit per test 5 seconds memory limit per test 256 megab ...

  9. 2022寒假集训day2

    day1:学习seach和回溯,初步了解. day2:深度优化搜索 T1 洛谷P157:https://www.luogu.com.cn/problem/P1157 题目描述 排列与组合是常用的数学方 ...

  10. ACM:HDU 2199 Can you solve this equation? 解题报告 -二分、三分

    Can you solve this equation? Time Limit: / MS (Java/Others) Memory Limit: / K (Java/Others) Total Su ...

随机推荐

  1. 天翼云存储资源盘活系统HBlock,全面释放企业数据价值

    9月6日,天翼云与科技媒体InfoQ联合举办的以"存储难题新解法,揭秘极/致易用的HBlock"为主题的线上技术分享会圆满落幕.天翼云国际业务事业部研发专家武志民与存储产品线总监魏 ...

  2. AtCoder [ABC351E] Jump Distance Sum 题解 [ 绿 ] [ 数学 ]

    原题 场上差点就想出来了,就差一个旋转坐标轴了. 初步分析 首先来看如何判断两个点能不能走到,这可以看成下面的一张网格图,相同颜色的格子相互连通. 因此根据瞪眼法,可以把这些格子分为 $ (x_i + ...

  3. SMMS图床Java接口上传

    前言 个人项目开发中,网站建设中需要用到大量的图片以及用户上传的图片,如果服务器带宽小,磁盘容量小将所有的图片信息全部存储在服务器上不太现实,这里建议将图片数据存储在对象存OSS上或者将图片保存在图床 ...

  4. OpenCascade 开源的三维建模几何造型开发平台

    官方文档 https://dev.opencascade.org/doc/overview/html/index.html Open CASCADE(简称OCC)平台是由法国Matra Datavis ...

  5. spring-boot配置slf4j日志

    SLF4J,即简单日志门面(Simple Logging Facade for Java),不是具体的日志解决方案,它只服务于各种各样的日志系统.按照官方的说法,SLF4J 是一个用于日志系统的简单 ...

  6. Docker 服务、镜像、容器简单命令使用

    Docker 进程相关命令 启动docker服务: systemctl start docker  查看docker服务状态: systemctl status docker 停止docker服务: ...

  7. Flink运行时架构

    一.运行时的组件和基本原理 1.作业管理器 (1)控制一个应用程序执行的主进程,也就是说,每个应用程序都会被一个不同的JobManager所控制执行. (2)JobManager会先接收到要执行的应用 ...

  8. Redis集群(cluster模式)搭建(三主三从)

    上一篇搭建了一主二从,并加入了哨兵,任何一个节点挂掉都不影响正常使用,实现了高可用.仍然存在一个问题,一主二从每个节点都存储着全部数据,随着业务庞大,数据量会超过节点容量,即便是redis可以配置清理 ...

  9. try except 案例

    def to_split(df): # 删除不符合加班统计的记录 try: df.dropna(subset=['姓名'], inplace=True) hang_index = df[df['加班信 ...

  10. Laravel11 从0开发 Swoole-Reverb 扩展包(一) - 扩展包开发

    前言 大家好呀,我是yangyang.好久没更新了,最近新项目在使用laravel11(截止目前发文,laravel12也发布了)做开发,自己也是利用有些空闲时间做些除开业务以外的深入学习,因此也就萌 ...