基础知识
二分查找的复杂度是log(n)的
STL中的二分查找函数
函数有哪些
binary_search(a, a+n, t) 查找 数组a 中是否存在 t ,其返回值为bool值
lower_bound(a, a+n, t) 返回可插入t的最小位置的迭代器,即返回第一处大于/等于 t 的位置的迭代器
a[7]={1, 2, 3, 3, 5, 6, 9};
lower_bound(a, a+7, 3); //其返回位置是第一个3的位置
lower_bound(a, a+7, 4); //其返回位置是第一个5的位置
upper_bound(a, a+7, 3); //其返回位置是第二个3的位置
upper_bound(a, a+7, 4); //其返回位置是第一个5的位置
upper_bound(a, a+n, t) 返回可插入的最大位置的迭代器,即返回第一处大于 t 的位置的迭代器
函数返回的迭代器怎么处理
迭代器其实相当于一个封装过的指针,所以要处理迭代器可以这样:
int i=lower_bound(a, a+n, t)-a;//此时 i 即为返回值的迭代器所对应的下标
即,使用函数返回值 减去 数组的数组名(代表其首地址)
例题:
NC24866
#include<stdio.h>
#include<algorithm>
using namespace std;
int main()
{
int a[7]={1, 2, 3, 3, 5, 6, 9};
int i=lower_bound(a, a+7, 3)-a;
printf("i:%d a[i]:%d", i, a[i]);
return 0;
}
二分答案+验证
先假设答案的值,带回去验证是否刚好成立,不符合则改变先前所假设的值,再进行判断,最终得出一个刚好满足条件的值
能解决什么问题
解决最大值最小(即让最大值尽可能小) 或者 最小值最大(即让最小值尽可能大)
解决这两类问题的常见方法为:
1.贪心(国王游戏)
2.二分
3.动态规划
解决这类问题是: 能二分尽量二分, 不能二分考虑贪心, 不能贪心考虑动态规划,都不能则大概率需要直接用暴力
例题:
NC107701、NOIP2015、NC24724
三分(单峰函数最大值、单谷函数最小值)
01分数规划
补充知识
1.差分数组(待复习)
使用条件
当有频繁的区间修改, 而每次都是对某个区间修改某个值的时候,可以使用差分数组
差分数组的定义
差分数组和 前缀和 一样,是通过对 原数组 进行数学运算得到的 新数组, 前缀和的定义规则是这样的:
我们先假设一个原数组 arr[5]={1, 3, 5, 7, 9};
那么我们可以定义一个前缀和数组 q[5]={1, 4, 9, 16, 25};
这个数组是如何定义实现的呢?其实是这样的:
q[0]=arr[0];
q[i]=q[i-1]+a[i]; 当 i != 0 时;
if(i==0)
q[i]==arr[i];
else
q[i]=q[i-1]+arr[i];
而差分数组的定义规则是这样的:
依然我们先假设一个原数组 arr[5]={1, 3, 4, 7, 10};
那么我们可以定义一个差分数组 c[5]={1, 2, 1, 3, 3};
差分数组的定义实现是这样的:
c[0]=arr[0]-0;
c[i]=arr[i]-arr[i-1]; 当 i != 0 时;
if(i==0)
c[i]=arr[i]-0;
else
c[i]=arr[i]-arr[i-1]
即,差分数组的每个元素是=原数组的对应元素-原数组的对应元素的前一项(因为我们定义的原数组是从0开始,所以差分数组第0项是=原数组的第0项-0)
差分数组的用法
现在我们定义好了一个差分数组,那么它对我们来说有什么用呢?
当我们需要让原数组 [l, r] 区间都加上 x 时,正常的处理是遍历数组的 l到r 区间,每次加上 x。这样做,时间复杂度太大了,所以我们可以使用差分,其处理方法是这样的:
对于区间的左边 l ,我们使 c[l]=c[l]+x
对应区间的右边 r,我们使 c[r+1]=c[r+1]-x
如此操作,我们便改变了差分数组在 [l,r] 区间上的值,但此时我们如果想使用原数组时,其实原数组的值都还未改变,若需要使用原数组改变后的值,那我们可以这样做:
遍历原数组arr[n]
a[0]=c[0]+0;
a[i]=c[i]+a[i-1]; 当 i != 0 时
其实这个操作是什么呢?就是差分数组定义的逆应用。这样我们就得到了一个改变后的数组。
2.主席树(函数式线段树 | 待学 )
3.线段树(待学)
4.尺取
尺取,就是在求满足条件的连续子区间时,通过移动子区间的左右边界去得到一个满足条件的区间
其具体实现操作是这样的:
我们先假设满足条件区间的左边界是 l, 右边界是 r。我们想去找满足条件的区间,则l 和 r应该从起点开始移动,即 l=r=0
接下了我们的操作是这样的,我们先判断当前区间满不满足条件。当当前[l, r] 区间不满足条件时, r++, 即右边界变大;当当前[l, r]区间满足条件时, l++, 即左边界变大。
最终,当我们遍历到l=n时,就结束了
5.unique函数(去重函数)
6.fined
7.setprecision();
有多(t)组测试数据时,一定要用while(t--)。写NC14301时,被坑死了,检查了所以可能错的地方,就是没发现用for循环时,内层和外层的循环变量是一样的。。。(直接哭晕在厕所了T_T)