Si consulta con frecuencia el valor máximo o mínimo de un intervalo cuando hace un problema, lleva mucho tiempo, pero si procesa previamente la tabla st (matriz dp), puede usar tiempo constante para consultar el valor máximo del intervalo
Definición del estado de la tabla ST
dp[i][j]
Representa el valor del elemento más grande en el intervalo que contiene 2 elementos j con subíndice i como el punto final izquierdo del intervalo
Es decir, dp[i][j]
= intervalo de [i, i+2^j-1]
valor es el elemento más grande de
La ventaja de esta definición es que para el punto final izquierdo de un intervalo, O(log(n))
el tiempo que pasamos puede enumerar el elemento más grande del intervalo con el punto final izquierdo y una potencia de 2 de longitud
Transición del estado de la tabla ST
Adoptamos la idea de dividir y conquistar. Si queremos consultar el valor máximo de un intervalo y dividir el intervalo en dos, entonces el valor máximo existe en la mitad izquierda o en la mitad derecha, y el resultado puede ser el máximo
Del mismo modo, supongamos que hemos obtenido todo el [subíndice i rango hasta el punto de izquierda, contiene 2 J 1- elementos de valor de intervalo del elemento más grande], entonces podemos combinar la cuestión:
Debido a que se encuentra el intervalo de 2 j-1 de longitud, entonces la longitud del intervalo combinado es 2 j , es fácil deducir la transición de estado: (suponiendo encontrar el valor máximo)
dp[i][j] = max(dp[i][j-1], dp[i+2^(j-1)][j-1])
La sección izquierda es [l es el punto final izquierdo y la longitud es 2 j-1 ] La
sección derecha es [l + 2 j-1 es el punto final izquierdo y la longitud es 2 j-1 ]
Debido a que 2 j = 2 j-1 + 2 j-1 , los dos subproblemas solo cubren el problema original , por lo que debemos usar la potencia de 2 como la unidad de iteración de longitud
Consulta de tabla ST
Solo conocemos el punto de partida de un punto arbitrario y el valor máximo del intervalo cuya longitud es una potencia de 2, pero ¿qué sucede si la longitud del intervalo de entrada no es una potencia de 2?
Pero sin Kansai, un intervalo de cualquier longitud siempre se puede descomponer en dos subintervalos de potencia 2 ( aunque los dos subintervalos se superponen ligeramente , el solapamiento no afecta la solución del problema final)
- Suponiendo que la longitud es len = 14, entonces la longitud de potencia máxima es sublen = 2 ^ log (2, len) = 8
- El subintervalo izquierdo es [punto final izquierdo, sublen-1], la respuesta 1 es
dp[left][log(2, len)]
- El subintervalo derecho es [punto final derecho -sublen + 1, punto final derecho], la respuesta es
dp[right-sublen+1][sublen]
int rmq(int l, int r)
{
if(l==r) return dp[l][0];
int len=(r-l+1), lg=log2(len), sublen=pow2(lg);
return max(dp[l][lg], dp[r-sublen+1][lg]);
}
Código
#include <bits/stdc++.h>
using namespace std;
#define maxn 1000
int dp[maxn][20], a[maxn], n;
int pow2(int x) {return 1<<x;}
int log2(int x) {return log((double)x)/log(2.0);}
int rmq(int l, int r)
{
if(l==r) return dp[l][0];
int len=(r-l+1), lg=log2(len), sublen=pow2(lg);
return max(dp[l][lg], dp[r-sublen+1][lg]);
}
int main()
{
// 读取数据,dp数组初始化,长度为 2^0=1 的区间最大值就是单个元素
cin>>n;
for(int i=0; i<n; i++)
cin>>a[i], dp[i][0]=a[i];
// dp递推,因为dp[i][j] 基于所有的dp[][j-1],所以外循环是j递增
for(int j=1; pow2(j)<=n; j++)
for(int i=0; i+pow2(j-1)<n; i++)
dp[i][j] = max(dp[i][j-1], dp[i+pow2(j-1)][j-1]);
// 暴力法,RMQ查询,验证正确性
for(int i=0; i<n; i++)
for(int j=i; j<n; j++)
cout<<"["<<i<<","<<j<<"] "<<a[max_element(a+i, a+j+1)-a]<<" "<<rmq(i, j)<<endl;
return 0;
}
/*
7
0 4 7 1 3 2 8
*/
Salida:
7
0 4 7 1 3 2 8
[0,0] 0 0
[0,1] 4 4
[0,2] 7 7
[0,3] 7 7
[0,4] 7 7
[0,5] 7 7
[0,6] 8 8
[1,1] 4 4
[1,2] 7 7
[1,3] 7 7
[1,4] 7 7
[1,5] 7 7
[1,6] 8 8
[2,2] 7 7
[2,3] 7 7
[2,4] 7 7
[2,5] 7 7
[2,6] 8 8
[3,3] 1 1
[3,4] 3 3
[3,5] 3 3
[3,6] 8 8
[4,4] 3 3
[4,5] 3 3
[4,6] 8 8
[5,5] 2 2
[5,6] 8 8
[6,6] 8 8