주어진 실수 ai,j(1≤i≤j≤N)에 대해 다음과 같이 정의된 di,j를 고려합니다.
di,j={ai.jmin{k∈{i,i+1,⋯,j−1}:di,k+dk+1,j+ai,j}if i=jif i<j
이는 다음과 같은 O(N3)짜리 동적계획법으로 해결할 수 있습니다.
for (int i=1;i<=N;i++){
D[i][i] = a[i][i];
}
for (int l=1;i<N;i++){ // l: length of interval
for (int i=1;i<=N-l;i++){
int j = i+l;
D[i][j] = INT_MAX;
for (int k=i;k<j;k++){
D[i][j] = min(D[i][j], D[i][k] + D[k+1][j] + a[i][j])
}
}
}
return D[1][N];
이것은 특정 조건을 만족하면 더 빠른 시간 안에 풀 수 있습니다. 위 코드에서 k의 검색 범위를 더 좁히는 방법이에요. 이를 위해서 ki,j를 다음과 같이 정의합니다:
ki,j={ik∈{i,i+1,⋯j−1} such that di,j=di,k+dk+1,j+ai,jif i=jif i<j
이렇게 di,j를 결정할 때 사용된 k값(인덱스)을 계속 저장해나갈 때, ki,j가 다음 조건을 만족하면 {di,j}는 O(N2)만에 계산 가능합니다.
ki,j−1≤ki,j≤ki+1,j
for (int i=1;i<=N;i++){
D[i][i] = a[i][i];
K[i][i] = i;
}
for (int l=1;i<N;i++){ // l: length of interval
for (int i=1;i<=N-l;i++){
int j = i+l;
D[i][j] = INT_MAX;
for (int k=K[i][j-1];k<=min(K[i+1][j],j);k++){
v = D[i][k] + D[k+1][j] + a[i][j];
if (v < D[i][j]){
D[i][j] = v;
K[i][j] = k;
}
}
}
}
return D[1][N];
배열 K를 만들어서 ki,j를 저장했고, 세 번째 루프에서 k의 검색 범위를 더 줄였습니다. 이 코드가 O(N2)임은 각 l에 대해 반복되는 연산이 많아야
i=1∑N−l(ki+1,j−ki,j−1+1)=i=1∑N−l(ki+1,i+l−ki,i+l−1+1)=i=2∑N−l+1ki,i+l−1−i=1∑N−lki,i+l−1+N−l=kN−l+1,N−ki,l+N−l<2N
이기 때문이에요. 이런 방식의 동적 계획법 최적화를 Knuth 최적화라고 합니다.