RMQ問題のSTアルゴリズム
ソース:http://www.cnblogs.com/ljsspace/archive/2011/08/04/2127317.html
彼のブログには多くのオンラインアルゴリズムがあります.本編は計算O(1)時間で無秩序配列のある区間の最小値を調べます.
ST(Sparse Table)アルゴリズムの基本的な考え方は、始点A[i]から長さ2のj乗(j=0,1...logn)の区間の最小値を計算してから、照会時にどの区間A[i.j]を2つの前処理が可能な重複区間に分割し、この2つの重複区間の最小値をとることである.
前処理段階では、始点A[i]から、どの長さが2^jの区間でも、2つの長さ2^(j-1)の区間に分けられます.ここで、最初の区間の範囲は、i...i+2^(j-1)-1です.第二の区間の範囲はi+2^(j-1)...i+2^j-1です.M[i,j]でA[i]から2^jの区間(つまりA[i]….A[i+2^j-1])の最小値に対応する下付きを表します.すると、A[M[i,j]=min{i……(j+2)-1]、A[i+2^(j-1)...i+2 j-1. DP思想を利用して、M[i,j−1]の値を先に計算し、M[i,j]の値を計算する.
照会段階では、任意の区間A[i.j]の長さd=j-i+1、k=flook(logd)を使用すれば、この区間は2つの長さ2^kのサブ区間によって完全にカバーされ、この2つの長さは2^kの区間に重複することができる.この二つの区間は前処理で最小値を求められているので、両者の最小値を取ってA[i.j]の最小値を得ることができる.
STアルゴリズム前処理段階の複雑さはO(nlogn)であり、クエリ段階の複雑さはO(1)である.
実装:
?
彼のブログには多くのオンラインアルゴリズムがあります.本編は計算O(1)時間で無秩序配列のある区間の最小値を調べます.
ST(Sparse Table)アルゴリズムの基本的な考え方は、始点A[i]から長さ2のj乗(j=0,1...logn)の区間の最小値を計算してから、照会時にどの区間A[i.j]を2つの前処理が可能な重複区間に分割し、この2つの重複区間の最小値をとることである.
前処理段階では、始点A[i]から、どの長さが2^jの区間でも、2つの長さ2^(j-1)の区間に分けられます.ここで、最初の区間の範囲は、i...i+2^(j-1)-1です.第二の区間の範囲はi+2^(j-1)...i+2^j-1です.M[i,j]でA[i]から2^jの区間(つまりA[i]….A[i+2^j-1])の最小値に対応する下付きを表します.すると、A[M[i,j]=min{i……(j+2)-1]、A[i+2^(j-1)...i+2 j-1. DP思想を利用して、M[i,j−1]の値を先に計算し、M[i,j]の値を計算する.
照会段階では、任意の区間A[i.j]の長さd=j-i+1、k=flook(logd)を使用すれば、この区間は2つの長さ2^kのサブ区間によって完全にカバーされ、この2つの長さは2^kの区間に重複することができる.この二つの区間は前処理で最小値を求められているので、両者の最小値を取ってA[i.j]の最小値を得ることができる.
STアルゴリズム前処理段階の複雑さはO(nlogn)であり、クエリ段階の複雑さはO(1)である.
実装:
?
/**
*
* Using ST(Sparse Table) algorithm to solve RMQ problem
* time complexity: <O(nlogn),O(1)>
*
*
* Copyright (c) 2011 ljs (http://blog.csdn.net/ljsspace/)
* Licensed under GPL (http://www.opensource.org/licenses/gpl-license.php)
*
* @author ljs
* 2011-08-02
*
*/
public
class
RMQ_ST {
//ST: O(nlogn) for preprocessing
public
int
[][] preprocess(
int
[] A){
int
n = A.length;
//floor value
int
maxJ=(
int
)(Math.log(n)/Math.log(
2
));
int
[][] M =
new
int
[n][maxJ+
1
];
//initial condition for dynamic programming: the RMQ for interval length=1
for
(
int
i =
0
; i < n; i++)
M[i][
0
] = i;
//dynamic programming: compute values from smaller(j=1) to bigger intervals
for
(
int
j =
1
; j<=maxJ; j++){
for
(
int
i =
0
; i + (
1
<< j) -
1
< n; i++){
int
nexti = i + (
1
<< (j -
1
));
if
(A[M[i][j -
1
]] <= A[M[nexti][j -
1
]])
M[i][j] = M[i][j -
1
];
else
M[i][j] = M[nexti][j -
1
];
}
}
return
M;
}
//ST: O(1) for querying
public
int
query(
int
[] A,
int
[][] M,
int
i,
int
j){
if
(j<i){
//swap i and j
int
tmp=i;i=j;j=tmp;
}
int
k = (
int
)(Math.log(j-i+
1
)/Math.log(
2
));
//the first interval
int
mina = M[i][k];
int
minb = M[j-(
1
<<k)+
1
][k];
if
(A[mina]<=A[minb])
return
mina;
else
return
minb;
}
public
static
void
main(String[] args) {
int
[] A=
new
int
[]{
0
,
1
,
2
,
3
,
7
,
1
,
9
,
2
,
8
,
6
};
RMQ_ST st =
new
RMQ_ST();
int
[][] M = st.preprocess(A);
System.out.format(
"%n***********************%n"
);
int
i=
3
,j=
7
;
int
min = st.query(A,M, i,j);
System.out.format(
"RMQ for A[%d..%d]: A[%d]=%d"
, i,j,min,A[min]);
System.out.format(
"%n***********************%n"
);
j=
3
;i=
7
;
min = st.query(A,M, i,j);
System.out.format(
"RMQ for A[%d..%d]: A[%d]=%d"
, i,j,min,A[min]);
System.out.format(
"%n***********************%n"
);
for
(
int
x=
0
;x<A.length;x++){
for
(
int
y=
0
;y<x;y++){
System.out.format(
" "
);
}
for
(
int
y=x;y<A.length;y++){
int
p = st.query(A,M,x,y);
System.out.format(
" %d/%d"
,A[p],p);
}
System.out.println();
}
}
}