朴素算法的最大问题就是每一次都需要一次
f
o
r
for
for来寻找最优解,那么可以考虑能否采用备忘录思想,将最优解存储下来,以便于
O
(
1
)
O(1)
O(1)的求出每一次查询的区间最值
设
d
p
[
i
]
[
j
]
dp[i][j]
dp[i][j]表示从第
i
i
i个元素到第
j
j
j个元素的区间最大值,设
D
a
t
a
[
i
]
Data[i]
Data[i]为第
i
i
i个元素,那么显然问题具有最优子结构性质,也就是
i
i
i到
j
j
j的区间最大值可以由
i
i
i到
j
−
1
j-1
j−1的区间最大值推出,所以很容易列出状态转移方程为
d
p
[
i
]
[
j
]
=
m
a
x
(
d
p
[
i
]
[
j
−
1
]
,
D
a
t
a
[
i
]
)
dp[i][j]=max(dp[i][j-1],Data[i])
dp[i][j]=max(dp[i][j−1],Data[i])这时候,查询时间复杂度是
O
(
1
)
O(1)
O(1),但这带来了一个新的问题,如果要查询第
1
0
6
10^6
106到
1
0
7
10^7
107元素之间的最大值,那么空间显然无法达到,也就是算法空间复杂度太高,同时,从时间复杂度上看,我们需要两圈
f
o
r
for
for循环,时间复杂度是
O
(
n
2
)
O(n^2)
O(n2),也不能够达到要求,需要考虑优化
倍增优化
考虑到上面算法的第二维设置成了区间终点,那么换一种思路,考虑设置为和区间长度相关的量,如果直接设置为区间长度,空间优化也不大,所以采用倍增思想,令
d
p
[
i
]
[
j
]
dp[i][j]
dp[i][j]表示为起点为
i
i
i,区间长度为
2
j
2^j
2j的区间的最大值,那么应该如何进行状态转移呢?
设需要查询的区间长度为
l
e
n
len
len,
s
i
z
e
=
l
o
g
2
(
l
e
n
)
size=log2(len)
size=log2(len),区间左端点为
l
l
l,右端点为
r
r
r,
d
p
[
i
]
[
j
]
dp[i][j]
dp[i][j]表示为起点为
i
i
i,区间长度为
2
j
2^j
2j的区间的最大值那么有
l
e
n
=
r
−
l
+
1
len=r-l+1
len=r−l+1,考虑将
d
p
[
i
]
[
j
]
dp[i][j]
dp[i][j]划分为这样两个区间,
d
p
[
i
]
[
j
−
1
]
dp[i][j-1]
dp[i][j−1]和
d
p
[
i
+
2
j
−
1
]
[
j
−
1
]
dp[i+2^{j-1}][j-1]
dp[i+2j−1][j−1],对应于查询区间为
[
i
,
i
+
2
j
]
[i,i+2^{j}]
[i,i+2j]划分为
[
i
,
i
+
2
j
−
1
]
[i,i+2^{j-1}]
[i,i+2j−1]和
[
i
+
2
j
−
1
]
[
i
+
2
j
]
[i+2^{j-1}][i+2^j]
[i+2j−1][i+2j],取这两个区间的最大值就是
d
p
[
i
]
[
j
]
dp[i][j]
dp[i][j]也就是状态转移方程为
d
p
[
i
]
[
j
]
=
m
a
x
(
d
p
[
i
]
[
j
−
1
]
,
d
p
[
i
+
2
j
−
1
]
[
j
−
1
]
)
dp[i][j]=max(dp[i][j-1],dp[i+2^{j-1}][j-1])
dp[i][j]=max(dp[i][j−1],dp[i+2j−1][j−1])当然,初值应该为
d
p
[
i
]
[
0
]
=
D
a
t
a
[
i
]
,
dp[i][0]=Data[i],
dp[i][0]=Data[i],这样
d
p
dp
dp数组就填充完了
接下来考虑如何使用
d
p
dp
dp数组,如果问题是求区间
[
l
,
r
]
[l,r]
[l,r]的最大值,需要在
[
l
,
r
]
[l,r]
[l,r]内进行一次划分,划分为
[
l
,
l
+
2
s
i
z
e
]
[l,l+2^{size}]
[l,l+2size]和
[
r
−
2
s
i
z
e
+
1
,
r
]
[r-2^{size}+1,r]
[r−2size+1,r]这样两个集合之间必然有交点,考虑集合两端点的大小可证明得到,这里略去,那么取这两部分的最大值也就得到了
d
p
[
i
]
[
j
]
dp[i][j]
dp[i][j],这样查询的时间复杂度就优化到了
O
(
1
)
O(1)
O(1)
这个
d
p
dp
dp数组就是ST表,将
d
p
dp
dp数组转换为ST数组如下
#include<iostream>#include<algorithm>#include<cstring>#include<cstdio>#include<vector>#include<cmath>#include<queue>#include<stack>#include<map>usingnamespace std;typedeflonglong ll;constint INF =0x3f3f3f3f;constint MAXN =1e6+100;constdouble eps =1e-6;int ST[MAXN][64];intmain(){//freopen("input.txt", "r", stdin);//freopen("output.txt", "w", stdout);int n, m, l, r;scanf("%d%d",&n,&m);for(int i=1;i<=n;i++)scanf("%d",&ST[i][0]);for(int j=1;j<=log2(n);j++){for(int i=1;i+(1<<(j -1))<= n;i++){
ST[i][j]=max(ST[i][j -1], ST[i +(1<<(j -1))][j -1]);}}while(m--){scanf("%d%d",&l,&r);int len = r - l +1;int sz =log2(len);printf("%d\n",max(ST[l][sz], ST[r -(1<< sz)+1][sz]));}return0;}