8.9 문제: Quantization ( 문제ID : QUANTIZE, 난이도: 중)

접근법

기존의 방법

quantize(A) = A 속한 수를 양자화해서 얻을  있는 최소 오차 제곱의 

변형된 방법

quantize(A,U) = U 지금까지   이상 사용한 숫자들의 집합일  A 속한 수를 양자화해서 얻을 
있는 최소 오차 제곱의 

답의 형태 제한하기

quantize(from, parts) = min [ minError(from, from + size -1) + quantize(from + size, parts -1) ]
    : from번째 이후의 숫자들을 parts개의 묶음으로 묶을 , 최소 오류 제곱 합을 반환하는 함수

minError(a,b) : a번째 숫자부터 b번째 숫자까지를 하나의 수로 표현했을 때의 최소 오류를 반환하는 함수

minError()의 구현

minError(a,b)에서 해야하는 일은 크게  가지 입니다.
1. 주어진 구간을 어떤 수로 표현해야 할지 결정하기
2. 결정한 m으로 해당 구간을 표현했을  오차를 계산하기
1. 주어진 구간 표현

img1

img2

img3

img4

img5

2. 오차 계산하기

img1

#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int inf = 987654321;
// A[]: 양자화해야 할 수열, 정렬한 상태
// pSum[]: A[]의 부분합을 저장한다. pSum[i]는 A[0] .. A[i]의 합
// pSqSum[]: A[]제곱의 부분합을 저장한다. pSqSum[i]는 A[0]^2 .. A[i]^2의 합
int n, m;
int A[101], pSum[101], pSqSum[101];
// A를 정렬하고 각 부분합을 계산한다.
void precalc(){
    sort(A, A+n);
    pSum[0] = A[0];
    pSqSum[0] = A[0] * A[0];
    for(int i = 1; i < n; i++){
        pSum[i] = pSum[i-1] + A[i];
        pSqSum[i] = pSqSum[i-1] + A[i]*A[i];
    }
}
// A[lo]..A[hi] 구간을 하나의 숫자로 표현할 때 최소 오차 합을 계산한다.
int minError(int lo, int hi){
    // 부분합을 이용해 A[lo] ~ A[hi]까지의 합을 구한다.
    int sum = pSum[hi] - (lo == 0 ? 0 : pSum[lo-1]);
    int sqSum = pSqSum[hi] - (lo == 0 ? 0 : pSqSum[lo-1]);
    // 평균을 반올림한 값으로 이 수들을 표현한다.
    int m = int(0.5 + (double)sum / (hi - lo + 1));
    int ret = sqSum -2 * m * sum + m * m * (hi - lo + 1);
    return ret;
}

int cache[101][11];
int quantize(int from, int parts){
    // 기저 사례: 모든 숫자를 다 양자화 했을 때
    if(from == n)return 0;
    // 기저 사례: 숫자는 아직 남았는데 더 묶을 수 없을 때 아주 큰 값을 반환한다.
    if(parts == 0)return inf;
    int& ret = cache[from][parts];
    if(ret != -1)return ret;
    ret = inf;
    // 조각의 길이를 변화시켜 가며 최소치를 찾는다.
    for(int partSize = 1; from + partSize <= n; partSize++)
        ret = min(ret, minError(from, from + partSize -1) + quantize(from + partSize, parts - 1));
    return ret;
}