二分与贪心
二分
二分法是什么
高中数学必修一对于二分法求方程近似解的介绍是:
对于在区间\([l,r]\)连续的函数\(f(x)\),若使得\(f(l)*f(r)<0\),则该区间内存在一变号零点(零点存在定理)
所以每次不断地将这个区间一分为二,每次判断两个子区间是否满足零点存在定理,即可求得零点的一个近似解
但是这样只能求得一组解
二分查找
在序列上查找一个元素的位置,最暴力的做法当然是便利这个序列,最坏的时间复杂度是\(O(n)\)的。
但是如果我们查找的序列是有序的,那么我们就可以运用二分法以\(O(log_2n)\)单次的复杂度进行查找
请注意,运用二分法通常有如下几个关键词:
【连续】 【单调】 【确切答案】
解通常是唯一的,或者干脆不存在
递归
int Binary_Search(int l,int r,int x)
{
if(l>r) return -1;
int mid=(l+r)/2;
if(a[mid]==x) return mid;
if(x<a[mid]) return Binary_Search(l,mid-1,x);
if(x>a[mid]) return Binary_Search(mid+1,r,x);
}
非递归
int Binary_Search(int l,int r,int x)
{
while(l<=r)
{
int mid=(l+r)/2;
if(a[mid]==x) return mid;
if(x<a[mid]) r=mid-1;
else l=mid+1;
}
return -1;
}
STL中的二分查找
-
std::lower_bound()
返回有序列表里第一个大于等于给定值的指针或迭代器
-
std::upper_bound()
返回有序列表里第一个大于给定值的指针或迭代器
-
std::binary_search()
返回给定值是否在有序表里存在
用法举例
- 【例1】
int x=lower_bound(a+1,a+n+1,y)-a-1;
一个非常常见的用途:离散化
void init()
{
for(int i=1;i<=n;i++)
a[i]=read(),b[i]=a[i];
sort(b+1,b+n+1);
for(int i=1;i<=n;i++)
a[i]=lower_bound(b+1,b+n+1,a[i])-a-1;//取下标
}
可以在值域较大的情况下,在保证相对大小不变的情况下缩小值域
这样很多值域相关的操作就可以女进行,例如树状数组逆序对
- 【例2】
int a[5]={0,2,4,6,8};
printf("%d %d %d\n",lower_bound(a,a+5,2)-a,upper_bound(a,a+5,2),binary_search(a,a+5,2));
printf("%d %d %d\n",lower_bound(a,a+5,3)-a,upper_bound(a,a+5,3),binary_search(a,a+5,3));
输出结果

在map/set里面的用法
a.lower_bound(2)
注意STL的迭代器不可减
毕竟这个本质是平衡树,哪来的下标,是吧
二分查找的实质
可以把数组看做值离散的函数
二分查找的实质就是定义在(定义域为整数的)函数的二分法
但是相比之下,二分查找对数组有单调性的要求,并且保证可以找到答案
二分答案
可以使用二分答案的题目的答案,会具有某种单调性
以及一些提示性的关键词:【最大值最小】【最小值最大】等
这类题目的思想大概就是:我们知道了答案满足这样的单调性,所以我们在答案的取值范围内进行二分,每次验证一个解
算法模板和之前的二分法求方程近似解非常像,不过会讨一个奇奇怪怪的函数检查生成的答案的正确性
通常来说,二分答案题目分为下列两种中的一种:
-
给定一个评价函数,求评价函数的最小/大值
-
给定一个条件,要求在满足条件的同时,使得代价最小
计算函数或者判断符合条件需要消耗极长的时间
或者说评价函数的值域太大了,不能一一计算
感性理解一下二分答案

模板
//假设我们这里要求最小值
int find(int l,int r)
{
while(l<r)
{
int mid=(l+r)/2;
if(check(mid)) r=mid;
else l=mid+1;
}
return -1;
}
//对于大多数题目而言,难点在于如何设计这个check()函数
贪心
什么是贪心
在每一步选择中都采取在当前状态下最好或最有(即最有利)的选择,从而希望导致结果是最好或最有的算法
说白了,这个算法的事情就和他的命自已样,每一步贪心的进行一种选择
当然,贪心不是具体的算法,而是一种思想
不资瓷一下吗QAQ

浙公网安备 33010602011771号