目录

  • 一、二分
    • 0.二分法
    • 1.整数域上的二分
    • 2.实数域上的二分
  • 二、三分
    • 0.三分求单峰函数极值
    • 1.P3382 【模板】三分法(秦九韶求多项式+三分)
  • 三、二分答案转化为判定
    • 0.经典例子
    • 1.POJ 2018 Best Cow Fences( 最佳牛围栏 )(二分答案)
      • (1)O(nlogn),二分+前缀和
      • (2)优化!O(n),二分+单调队列
    • 2.AcWing 113. Innovative Business(特殊排序)
    • 3.K-th Number (CCPC.2017)(二分+尺取)

声明:
本系列博客是《算法竞赛进阶指南》+《算法竞赛入门经典》+《挑战程序设计竞赛》的学习笔记,主要是因为我三本都买了 按照《算法竞赛进阶指南》的目录顺序学习,包含书中的少部分重要知识点、例题解题报告及我个人的学习心得和对该算法的补充拓展,仅用于学习交流和复习,无任何商业用途。博客中部分内容来源于书本和网络(我尽量减少书中引用),由我个人整理总结(习题和代码可全都是我自己敲哒)部分内容由我个人编写而成,如果想要有更好的学习体验或者希望学习到更全面的知识,请于京东搜索购买正版图书:《算法竞赛进阶指南》——作者李煜东,强烈安利,好书不火系列,谢谢配合。


下方链接为学习笔记目录链接(中转站)

学习笔记目录链接


ACM-ICPC在线模板


一、二分

0.二分法

二分是一个非常常见但是想要精通却很有讲究的一个垃圾 算法,二分的基础用法是在单调序列或单调函数中进行查找(二分查找),因此当问题的答案具有单调性,就可以通过二分把求解答案的问题转换成二分查找答案并判定答案的正确性(二分答案)。我们还可以用三分法去求解单峰函数的极值。

相信基础的二分大家都会,但是写出来二分不代表能A,因为二分的细节尤其重要,对于整数域上的二分,要注意终止边界和左右区间的取舍时的开闭情况,避免溜掉答案或造成死循环。对于实数域上的二分,要注意精度的问题。(我甚至在学习这一节之前,写的二分都是看运气,随缘调试,随缘AC,根本都没有注意到有这么多细节 )

1.整数域上的二分

本文中的二分保证最终答案处于闭区间 [l,r][l,r][l,r] 以内,并以 l=rl=rl=r 作为循环的结束条件

在单调递增序列a中查找>=x的数中最小的一个(即x或x的后继)


while (l < r) {int mid = (l + r) / 2;if (a[mid] >= x) r = mid; else l = mid + 1;
}

在单调递增序列a中查找<=x的数中最大的一个(即x或x的前驱)


while (l < r) {int mid = (l + r + 1) / 2;if (a[mid] <= x) l = mid; else r = mid - 1;
}

作者给出的一套二分的流程:

  1. 分析问题,确定左右半段哪一个是可行区间,以及 midmidmid 归属哪一半段
  2. 根据分析结果,选择r=mid,l=mid+1,mid=(l+r)>>1l=mid,r=dmi-1,mid=(l+r+1)>>1
  3. 二分终止条件是l==r,该值就是答案所在的位置。

2.实数域上的二分

实数域二分,设置eps法

一般eps=10−(k+2)eps=10^{-(k+2)}eps=10−(k+2)(k是需要保留的位数)

while (l + eps < r) {double mid = (l + r) / 2;if (calc(mid)) r = mid; else l = mid;
}

实数域二分,规定循环次数法

for (int i = 0; i < 100; i++) {double mid = (l + r) / 2;if (calc(mid)) r = mid; else l = mid;
}

二、三分

0.三分求单峰函数极值

有一类函数称为单峰函数,它们拥有唯一的极大值点,在极大值点左侧严格单调上升,在极大值点右侧严格单调下降;或者拥有唯一的极小值点,在极小值点左侧严格单调下降,在极小值点右侧严格单调上升。

为了避免混淆,我们称后一种函数为单谷函数,对于单峰或单谷函数,可以用三分求其极值。

以单峰函数f()f()f()为例,我们在函数的定义域[l,r][l,r][l,r]上人去两个点lmidlmidlmid和rmidrmidrmid,其中l<limd<rmid<rl<limd<rmid<rl<limd<rmid<r,把函数分成三段:

如果f(lmid)<f(rimd)f(lmid)<f(rimd)f(lmid)<f(rimd),则lmidlmidlmid和rmidrmidrmid要么同时处于极大值点左侧,要么分别位于极大值点两侧,无论是哪种情况,都可以确定极大值点在lmidlmidlmid右侧,可令l=lmidl=lmidl=lmid。

如果f(lmid)>f(rmid)f(lmid)>f(rmid)f(lmid)>f(rmid),则lmidlmidlmid和rmidrmidrmid要么同时处于极大值点右侧,要么分别位于极大值点两侧,无论是哪种情况,都可以确定极大值点在rimdrimdrimd左侧,可令r=rmidr=rmidr=rmid。

如果在三分中遇到了lmid=rmidlmid=rmidlmid=rmid,对于严格单调的函数,此时取l=lmidl=lmidl=lmid或者r=rmidr=rmidr=rmid 均可,对于非严格单调的函数,此时三分法不再适用。

1.P3382 【模板】三分法(秦九韶求多项式+三分)

#include<iostream>
#include<string.h>
#include<cstdio>
#include<math.h>
#include<map>
#define ls (p<<1)
#define rs (p<<1|1)
#define over(i,s,t) for(register long long i=s;i<=t;++i)
#define lver(i,t,s) for(register long long i=t;i>=s;--i)
//#define int __int128
using namespace std;
typedef long long ll;//全用ll可能会MLE或者直接WA,全部换成int看会不会A,别动这里!!!
const ll N=110000;
const ll mod=1e9+7;
const double EPS=1e-10;//-10次方约等于趋近为0ll n,m;
double a[20],l,r;//神奇的秦九韶公式求多项式
//背!
inline double f(double x)
{double ans=0;lver(i,n,0){ans=ans*x+a[i];}return ans;
}
int main()
{cin>>n>>l>>r;lver(i,n,0)scanf("%lf",&a[i]);while(l+EPS<r){double x=(2*l+r)/3;//三分double y=(2*r+l)/3;if(f(x)>f(y))r=y;else l=x;}printf("%.5f\n",l);//输出要用freturn 0;
}

三、二分答案转化为判定

一个宏观的最优化问题也可以抽象为函数,其定义域是该问题下的可行方案,对这些可行方案进行评估得到的数值构成函数的值域,最优解就是评估最优方案(不妨设评估越高越优)。假设最优评分是S,显然对于所有>S>S>S的值,都不存在一个合法的方案达到此评分,否则就与S的最优性矛盾,而对于所有的<S<S<S,一定存在一个合法方案达到或超过此评分。这样的问题的值域就具有一种特殊的单调性,在S的一侧合法,另一侧不合法,就像一个在(−∞,S](-\infty, S](−∞,S]上为1,在(S,∞)(S, \infty)(S,∞)上值为0的分段函数,可以通过二分找到这个分界点S。

借助二分,我们把求解最优解问题,转化为给定一个值midmidmid,判断是否存在一个可行方案评分达到mid的问题。
看不太懂没关系,一看代码就明白了,主要理解思路

0.经典例子

有N本书排成一行,已知第i本的厚度是AiA_iAi​ 。把它们分成连续的M组,使T最小,其中T表示厚度之和最大的一组的厚度。

//把n本书分成m组,其中每一组的厚度之和都<=size,判断是否可行
bool valid(int size)
{int group = 1, reset = size;for (int i = 1; i <= n; ++i){if (reset >= a[i])//如果放得下就继续放reset -= a[i];//放不下就换一组新的else group++,reset = size - a[i]; }return group <= m;
}
int main()
{int l = 0, r = sum;while (l < r){int mid = (l + r) >> 2;if (valid(mid)) r = mid;else l = mid + 1;}cout << l << endl;return 0;
}

1.POJ 2018 Best Cow Fences( 最佳牛围栏 )(二分答案)

https://www.acwing.com/problem/content/104/

输入样例:

10 6
6
4
2
10
3
8
5
9
4
1

输出样例:

6500

问题可以简化为一正整数数列,求一个平均数最大,长度不小于L的连续的子段。
那么就可以二分答案,把问题转化为判定“是否存在一个长度不小于L的子段,平均数不小于二分的值mid”。如果我们直接把数列的每一个数都减去二分的值,那么问题又变成了“是否存在一个长度不小于L的子段,子段和非负”。

(1)O(nlogn),二分+前缀和

比较简单的写法

#include<iostream>
#include<string.h>
#include<cstdio>
#include<math.h>
#include<map>
#define ls (p<<1)
#define rs (p<<1|1)
#define over(i,s,t) for(register long long i=s;i<=t;++i)
#define lver(i,t,s) for(register long long i=t;i>=s;--i)
//#define int __int128
using namespace std;
typedef long long ll;//全用ll可能会MLE或者直接WA,全部换成int看会不会A,别动这里!!!
const ll N=100001;
const ll mod=1e9+7;
const double EPS=1e-5;//-10次方约等于趋近为0double a[N],b[N],sum[N];
int n,L;
inline bool check(double x){over(i,1,n)b[i]=a[i]-x;over(i,1,n)sum[i]=sum[i-1]+b[i];double ans=-1e10;double min_val=1e10;//注意是长度不小于L,所以可以大于L,取不小于L中的最大值for(int i=L;i<=n;++i){min_val=min(min_val,sum[i-L]);ans=max(ans,sum[i]-min_val);}return ans>=0;
}int main()
{cin>>n>>L;over(i,1,n)scanf("%lf",&a[i]);double l=-1e6,r=1e6;while(l+EPS<r){double mid=(l+r)/2;if(check(mid))l=mid;else r=mid;}cout<<int(r*1000)<<endl;return 0;
}

(2)优化!O(n),二分+单调队列

下面这道题跟本题基本类似,只是多了个右界的限制,删掉就好,然乎就一摸一样了。

【题解】P1419 寻找段落(二分+单调队列)难度⭐⭐⭐★
https://blog.csdn.net/weixin_45697774/article/details/104582688

2.AcWing 113. Innovative Business(特殊排序)

innovation n.创新,革新
原题链接:https://www.acwing.com/problem/content/description/115/

输入样例

[[0, 1, 0], [0, 0, 0], [1, 1, 0]]

输出样例

[3, 1, 2]

题解来源:https://www.acwing.com/solution/AcWing/content/1009/

// Forward declaration of compare API.
// bool compare(int a, int b);
// return bool means whether a is less than b.class Solution {public:vector<int> specialSort(int N) {vector<int>res;res.push_back(1);for(int i=2;i<=N;++i){int l=0,r=res.size()-1;while(l<=r){int mid=l+r>>1;if(compare(res[mid],i))l=mid+1;else r=mid-1;}res.push_back(i);for(int j=res.size()-2;j>r;j--)swap(res[j],res[j+1]);}return res;}
};

3.K-th Number (CCPC.2017)(二分+尺取)

链接:https://ac.nowcoder.com/acm/contest/19/B
来源:牛客网

题目描述

Alice are given an array A[1…N] with N numbers. Now Alice want to build an array B by a parameter K as following rules: Initially, the array B is empty. Consider each interval in array A. If the length of this interval is less than K, then ignore this interval. Otherwise, find the K-th largest number in this interval and add this number into array B. In fact Alice doesn’t care each element in the array B. She only wants to know the M-th largest element in the array B. Please help her to fi nd this number.
输入描述:
The first line is the number of test cases. For each test case, the first line contains three positive numbers N(1≤N≤105);K(1≤K≤N);M. The second line contains N numbers Ai(1≤Ai≤109).It’s guaranteed that M is not greater than the length of the array B.
输出描述:
For each test case, output a single line containing the M-th largest element in the array B.

示例1

输入

2
5 3 2
2 3 1 5 4
3 3 1
5 8 2

输出

3
2

题目大意:
给你数列A,对于A的每一个区间,求第K大,插入到数列B中,最后再求数列B的第M大
题目详解:

https://blog.csdn.net/codeswarrior/article/details/82827902

#include<iostream>
#include<string.h>
#include<cstdio>
#include<math.h>
#include<map>
#define ls (p<<1)
#define rs (p<<1|1)
#define over(i,s,t) for(register long long i=s;i<=t;++i)
#define lver(i,t,s) for(register long long i=t;i>=s;--i)
//#define int __int128
using namespace std;
typedef long long ll;//全用ll可能会MLE或者直接WA,全部换成int看会不会A,别动这里!!!
const ll N=100001;
const ll mod=1e9+7;
const double EPS=1e-5;//-10次方约等于趋近为0ll t,n,m,k;
ll a[N];bool check(ll x){ll sum=0,s=0;for(int i=0,j=0;j<n;++j){if(a[j]>=x)sum++;if(sum==k){s+=n-j;while(a[i]<x){s+=n-j,i++;}sum--,i++;}}return s>=m;}
int main()
{cin>>t;while(t--){cin>>n>>k>>m;over(i,0,n-1)scanf("%lld",&a[i]);ll l=1,r=1e9+10;while(l<r){ll mid=(l+r)>>1;if(check(mid))l=mid+1;else r=mid;}printf("%lld\n",l-1);}return 0;
}

注:如果您通过本文,有(qi)用(guai)的知识增加了,请您点个赞再离开,如果不嫌弃的话,点个关注再走吧,日更博主每天在线答疑 ! 当然,也非常欢迎您能在讨论区指出此文的不足处,作者会及时对文章加以修正 !如果有任何问题,欢迎评论,非常乐意为您解答!( •̀ ω •́ )✧

0x04.基本算法 — 二分和三分相关推荐

  1. 信息学奥赛一本通 提高篇 第一部分 基础算法 第2章 二分与三分

    信息学奥赛一本通 提高篇 提高版 第一部分 基础算法 第2章 二分与三分 信息学奥赛一本通 提高篇 提高版 第一部分 基础算法 第2章 二分与三分_mrcrack的博客-CSDN博客_信息学奥赛一本通 ...

  2. java 二分查找_计算机入门必备算法——二分查找法

    1.引言 笔者对于计算机的研究一直停滞不前,近期想对一些算法进行复习和进一步的研究,每天都会更新一个新的算法,算法有难有易,层层递进.不希望能学的有多么高深,只希望在一些最基本的算法上有编码的思路,或 ...

  3. k均值算法 二分k均值算法_如何获得K均值算法面试问题

    k均值算法 二分k均值算法 数据科学访谈 (Data Science Interviews) KMeans is one of the most common and important cluste ...

  4. k均值算法 二分k均值算法_使用K均值对加勒比珊瑚礁进行分类

    k均值算法 二分k均值算法 Have you ever seen a Caribbean reef? Well if you haven't, prepare yourself. 您见过加勒比礁吗? ...

  5. (算法-二分)浮点数二分-求一个数的三次方根

    (算法-二分)浮点数二分-求一个数的三次方根 题目描述 题目分析 误差分析 二分(当n>=1时) (当0<=n<1时) (当n<0时) 解决方案 题目描述 给定一个浮点数 n, ...

  6. 2020ICPC上海 D Walker(思维、二分、三分)

    链接 题目描述 As a world-famous traveler, Prof. Pang's research interest is to travel as many places as po ...

  7. win10分屏设置一边竖屏_Win10多窗口“二分屏/三分屏/四分屏”显示技巧

    有时候,我们在同时运行多个任务时,需要把这几个窗口同时显示在屏幕上,这样操作分比较方便,而且可以避免频繁切换窗口的麻烦.例如:我们想要把一个文件夹中的个别文件移动到另一个文件夹中,这时同时打开这两个文 ...

  8. 减治法解决八枚硬币问题/假币问题(JAVA)----二分,三分,不知轻重的情况

    八枚硬币问题 在八枚外观相同的硬币中,有一枚是假币,并且已知假币与真币的重量不同,但不知道假币与真币相比较轻还是较重.可以通过一架天平来任意比较两组硬币,设计一个高效的算法来检测出这枚假币. 我们先假 ...

  9. ACM——二分、三分

    二分算法模板: 二分的思想不说了,在这里主要写一下自己的模板. 1.算法竞赛中: 2.ACwing--yxc bool check(int x) {/* ... */} // 检查x是否满足某种性质/ ...

最新文章

  1. 微软私有云Azure Pack实践系列之三创建虚拟机角色
  2. 迷你书下载(持续更新中。。。)
  3. TD8620手持数字特斯拉计一些基本的定标
  4. java 图片上传
  5. sklearn:sklearn.feature_selection的SelectFromModel函数的简介、使用方法之详细攻略
  6. git怎么上传文件到别人的仓库_Git将本地代码上传至远程仓库
  7. CentOS 7 + nginx-1.12 + php-7.2 + MySQL-5.7
  8. rename、remove
  9. Pytest-ordering自定义用例执行顺序
  10. CC++中的时间函数和时间类
  11. WPF地区选择控件(内附下载地址)
  12. DirectFB简介以及移植[一]【转】
  13. 从前端页面上下载为png格式的图片
  14. 从裸机到嵌入式Linux—总纲
  15. JN5169 ZigBee3.0软硬件开发环境
  16. HBuilderX连接不上海马玩模拟器
  17. 密码学读书笔记系列(三):《商用密码应用与安全性评估》
  18. 点分治+CDQ分治+整体二分全纪录
  19. 追寻着最初的梦想,我们上路吧
  20. Rancher 添加主机失败的解决方法

热门文章

  1. OpenCV的实用图像处理操作案例分享
  2. Facebook开源高效图像Transformer,速度、准确率与泛化性能媲美SOTA CNN
  3. Task01——零基础入门NLP - 新闻文本分类之赛题理解
  4. SPI flash配置
  5. Eclipse中配置约束
  6. 哈希一致性、哈希取余、自定义轮询分片的比较
  7. 基于网络监听方式的电子邮件实现基础
  8. 在Linux中打印函数调用堆栈【原创】
  9. http状态码302,303,307的区别
  10. .NET : 再谈谈XML中的命名空间问题