算法
贪心
顾名思义,做题的时候要贪心
,要考虑怎么决策能得到当前状态的最优值
和以前学的相比,最大的收获是若按照题目给的思路不能得到明确的思路,即要正难则反
,转变思路,从另一方面考虑
另外,贪心
有难有易,有可能结合别的算法一起呈现,比如二分判定答案可能就会用到贪心
因为贪心
的思路只能考虑当前的状态,所以使用的时候要慎重
堆 (新算法)
时间复杂度( l o g N logN logN)
1. 定义
如果有一个关键码的集合K = {k0,k1, k2,…,kn-1},把它的所有元素按完全二叉树的顺序存储方式存储在一个一维数组中,并满足:Ki <= K2i+1 且 Ki<=K2i+2 ,则称为小堆(或大堆)。
将根节点最大的堆叫做最大堆或大根堆,根节点最小的堆叫做最小堆或小根堆。
新的一种数据结构,用来自动排序,默认的是从大到小,即堆顶的元素是最大值(大根堆)
堆这样定义
priority _ queue < int > q;
这里注意,c++里默认定义的堆为大根堆
定义小根堆需要用自带的greater/less函数
使用方法如下
priority_queue <int,vector<int>,greater<int> > q;//小根堆
priority_queue <int,vector<int>,less<int> > q;//大根堆
注:在<>里最后一个右箭头前要记得打一个空格,否则会被识别成 c i n > > cin>> cin>>,导致错误
2. 结构
如果记不住小根堆如何定义,也可以重载运算符
,类似
s
o
r
t
sort
sort 时的
m
y
c
m
p
mycmp
mycmp——自定义的比较方式
格式如下:
struct node
{
int zhi,id,x;
friend bool operator < (node n1,node n2)
{
return n1.zhi>n2.zhi;
}
}hanshu[10050];
priority_queue<node> q;
重载运算符
比
s
o
r
t
sort
sort 运用更广
3. 算法实现理解
4. 堆的常用方法
- 构建优先队列
- 支持堆排序
- 快速找出一个集合中的最小值(或最大值)
5. 对顶堆
就是一个大根堆和一个小根堆的堆顶相对,构成一个整体上单调的数据结构
如上图所示,我们能轻易地从中确定中位数
的位置,所以可以和中位数
一起用
二分
本质上就是将答案范围缩小的过程,与倍增互逆
1. 二分答案
即解题时枚举答案然后检验枚举的值是否正确
若我们枚举的答案与题目关键条件具备有序的特征,我们把这里的枚举换成二分
,就变成了二分答案
二分答案的思想:将一个求最优解或者最值问题,转换为一个验证性问题
特征:题目要求的是最大的最小值,最小的最大值
二分答案的边界处理代码如下:
while(l+1<r)
{
mid=(l+r)/2;
if(check(mid)) l=mid;
else r=mid;
}
//如果要求的是最小值
if(check(l)) ans=l;//答案就是l
else ans=r;//答案就是r
倍增
倍增
是根据已经得到的信息,将考虑的范围扩大一倍,从而加速操作的一种思想
使用了倍增思想的算法:
a.归并排序
b.快速幂
c.基于ST表的RMQ算法
这里只讨论c++.基于ST表的 R M Q RMQ RMQ 算法
基于ST表的RMQ算法
R
M
Q
(
R
a
n
g
e
M
i
n
i
m
u
m
/
M
a
x
i
m
u
m
Q
u
e
r
y
)
RMQ(Range Minimum/Maximum Query)
RMQ(RangeMinimum/MaximumQuery),即区间最值查询
,是指这样一个问题:对于长度为n的数列A,回答若干询问RMQ(A,i,j),返回数列A中下标在i,j之间的最小/大值。
令 A [ i ] A[i] A[i] 存储我们要求最值的数列,设立 F [ i ] [ j ] F[i][j] F[i][j] 表示从第 i i i 个位置开始,往后的 2 j 2^j 2j 个数字中最大的数字
如何预处理出F数组?
• 我们对j从小到大枚举。每次处理 f [ i ] [ j ] f[i][j] f[i][j] 的时候, f [ i ] [ 0 j − 1 ] f[i][0~j-1] f[i][0 j−1] 都已经处理好了
• f [ i ] [ j ] = m a x ( f [ i ] [ j − 1 ] , f [ i + 2 j − 1 ] [ j − 1 ] ) f[i][j]=max(f[i][j-1],f[i+2^{j−1}][j-1]) f[i][j]=max(f[i][j−1],f[i+2j−1][j−1])
• 因为 j j j 最大到 l o g 2 n log_2n log2n ,所以预处理的复杂度为 O ( n l o g 2 n ) O(nlog_2n) O(nlog2n)
for(int i=1;i<=n;i++)
cin>>f[i][0];
for(int i=1;(1<<j)<=n;j++)
for(int i=1;i<=n;i++){
f[i][j]=f[i][j-1];
if(((1<<(j-1))+i)<=n)
f[i][j]=min(f[i][j],f[i+(1<<(j-1))][j-1];
}
如何查询?询问区间长度不满足 2 2 2 的整数次幂怎么办?
很简单,设查询区间为 ( i , j ) (i,j) (i,j),令 k = l o g 2 ( j − i + 1 ) k = l o g 2 ( j − i + 1 ) k=log_2(j−i+1)k=log_2(j−i+1) k=log2(j−i+1)k=log2(j−i+1),那么要查询的结果就是: m a x ( f [ i ] [ k ] , f [ j − 2 k + 1 ] [ k ] max(f[i][k],f[j−2^{k}+1][k] max(f[i][k],f[j−2k+1][k],这样一定能够保证覆盖整个区间
int ask(int x,int y){
int k= log2(y-x+1);
return max(f[x][k] , f[y-(1<<k)+1][k];
}