一、搜索
1.迭代加深
优化dfs,从小到大限制搜索的深度,如果在当前深度限制下搜不到答案,就把深度限制增加,重新进行一次搜索。适用于搜索树规模随着层次的深度增长很快,并且能保证答案在一个较浅层的节点。
2.估价函数
优先队列bfs:维护了一个优先队列,不断从堆中取出当前代价最小的状态(堆顶)进行扩展,每个状态第一次从堆顶取出时,就得到了初态到当前的最小代价。如果给定一个目标状态,需要求出从初态到目标态的最小代价,可以设计估价函数,以任意状态为输入,计算出从当前状态到目标状态所需代价的估价值。维护一个堆,不断取出当前代价+未来估价最小的状态进行扩展。估价函数的估值不能大于未来实际代价。
3.IDA*
估价函数与迭代加深的dfs相结合,设计一个估价函数,估算从每个状态到目标状态需要的步数,若当前深度+未来估计步数>深度限制,则立即从当前分支回溯。
二、贪心
对于排列型表示经常考虑交换相邻元素的影响(邻项交换)。
决策包容性:证明在任何局面下,做出局部最优决策后,在问题状态空间中的可达集合包含了做出其他任何决策后的可达集合。
范围缩放:证明任何对局部最优策略作用范围的扩展都不会导致整体结果变差。
三、二分
四、倍增
1.快速幂&慢速乘
1 unsigned long long mul(unsigned long long a,unsigned long long b) { 2 unsigned long long ans = 0; 3 while(b) { 4 if(b&1)ans=(ans+a)%mod; 5 a=(a+a)%mod; 6 b>>=1; 7 } 8 return ans; 9 } 10 unsigned long long power(unsigned long long a,unsigned long long b) { 11 unsigned long long ans=1; 12 while(b) { 13 if(b&1)ans=mul(ans,a)%mod; 14 a=mul(a,a)%mod; 15 b>>=1; 16 } 17 return ans; 18 }
2.矩阵快速幂
前置技能:矩阵乘法
3.ST 表
解决无修改的区间最值问题,O(NlogN)预处理,O(1)查询。
设f[i][j]表示数列A中下标在子区间[i,i + 2^j -1] 的数的最大值。
在递推时,我们把子区间的长度成倍增长,有公式F[i,j]=max(F[i,j-1].F[i+2^j,j-1),即长度为2^j的子区间的最大值是左右两半长度为2^j-1的子区间的最大值中较大的一个。
当询问任意区间[L,r]的最值时,我们先计算出一个k,满足2^k<r-l+1<2^k+1,也就是使2的k次幂小于区间长度的前提下最大的k。
那么“从l开始的2^k个数”和“以r结尾的2^k个数”这两段一定覆盖了整个区间[L,r],这两段的最大值分别是F[L,k]和F[r-2^k+1,k],二者中较大的那个就是整个区间[L,r]的最值。
因为求的是最大值,所以这两段只要覆盖区间[L,r]即可,即使有重叠也没关系。
(此处代码求的为最小值)
1 #include<iostream> 2 #include<cstdio> 3 #include<cmath> 4 #include<string> 5 #include<cstring> 6 #include<algorithm> 7 #include<iomanip> 8 using namespace std; 9 namespace Moxing{ 10 const int N=1e5+5; 11 int m,n,t;long long f[N][18],a[N]; 12 void ST_prework(){ 13 for(int i = 1; i <= n; i++){ 14 f[i][0] = a[i]; 15 } 16 t=log(n)/log(2)+1; 17 for(int i = 1; i <= t; i++){ 18 for(int j = 1; j <= n-(1<<i)+1; j++){ 19 f[j][i] = min(f[j][i-1],f[j+(1<<(i-1))][i-1]); 20 } 21 } 22 } 23 long long ST_query(int l,int r){ 24 int k = log(r-l+1)/log(2); 25 return min(f[l][k],f[r-(1<<k)+1][k]); 26 } 27 struct main{ 28 main(){ 29 memset(f,0x3f,sizeof(f)); 30 scanf("%d%d",&n,&m); 31 for(int i = 1; i <= n; i++){ 32 scanf("%lld",&a[i]); 33 } 34 ST_prework(); 35 int l,r; 36 for(int i = 1; i <= m; i++){ 37 scanf("%d%d",&l,&r); 38 printf("%lld ",ST_query(l,r)); 39 } 40 exit(0); 41 } 42 }UniversalLove; 43 } 44 int main(){ 45 Moxing::main(); 46 }
4.LCA
5.其他
在递推时,我们把子区间的长度成倍增长,有公式F[,]=max(F[i,j-1].F[i+21j-11),即长度为2/的子区间的最大值是左右两半长度为2/-1的子区间的最大值中较大的一个。当询问任意区间[L,r]的最值时,我们先计算出一个k,满足2*<r-l+1s2*+1,也就是使2的k次幂小于区间长度的前提下最大的k。那么“从l开始的24个数”和“以r结尾的2*个数”这两段一定覆盖了整个区间[L,r],这两段的最大值分别是FL,k]和F[r-2*+1,k],二者中较大的那个就是整个区间L,r]的最值。因为求的是最大值,所以这两段只要覆盖区间[L,r]即可,即使有重叠也没关系,