KoreanFoodie's Study
[종만북 문제] Quantization (문제 ID : QUANTIZE, 난이도 : 중) 본문
Data Structures, Algorithm/종만북
[종만북 문제] Quantization (문제 ID : QUANTIZE, 난이도 : 중)
GoldGiver 2024. 3. 11. 22:55
프로그래밍 대회에서 배우는 알고리즘 문제해결 전략(이하 종만북)에서 소개된 문제를 풀이합니다. 알고리즘에 진심이시라면, 직접 구매하셔서 읽어보시는 것을 추천합니다!
핵심 :
1. 최적 부분 문제를 찾는 것은 간혹, 매우 쉽지 않은 일이다... 무식하게 푸는 전법부터 시작하면 큰코가 다칠 수 있다. 근데 나 코 낮은데.. 어쨌든 다쳤다.
[종만북 문제] Quantization (문제 ID : QUANTIZE, 난이도 : 중)
이 문제는 아이디어가 필요하다. 단순히 수열을 정렬한 다음에, 숫자를 최솟값에서 최댓값까지 넣는 방식으로는 풀 수 없기 때문이다.
그런데 이렇게 묶어보면 어떨까?
이렇게 묶어 놓고 보면, 인접한 숫자끼리 적절히 묶고, 최소 오차를 내는 수를 찾는 문제로 변형할 수 있다!
그럼 아래와 같이 점화식을 세워볼 수 있는데...
minError 는 한 묶음 내에서 오차를 구하는 함수이다. from 은 시작 지점, parts 는 사용 가능한 남은 양자화 숫자의 수를 의미한다.
그렇다면 minError 는 어떻게 구할 수 있을까?
위처럼 식을 세울 수 있는데... 2차함수니까, 미분을 통해 최솟값을 구하면 될 것이다(미분값이 0 인 지점).
또한, 부분합의 경우 아래의 원리를 이용하면 된다 :
부분합 누적 수열을 구하고, 구간을 구할 때 끝 인덱스 값에서 시작 인덱스 값을 빼주기만 하면 된다. 😉
따라서 위의 내용을 전부 적용하면, 아래와 같이 코드를 구현할 수 있다! 😊
#include <iostream>
#include "stdlib.h"
#include <cmath>
#include <vector>
#include <map>
#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;
int s;
int A[101], pSum[101], pSqSum[101];
int cache[101][11];
// 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));
// sum(A[i] - m)^2 전개한 결과를 부분 합으로 표현
int ret = sqSum - 2 * m * sum + m * m * (hi - lo + 1);
return ret;
}
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;
}
void inputHandler()
{
cin >> n;
cin >> s;
for (int i = 0; i < n; ++i)
{
cin >> A[i];
pSum[i] = 0;
pSqSum[i] = 0;
for (int j = 0; j <= s; ++j)
cache[i][j] = -1;
}
precalc();
}
int main()
{
int cases;
cin >> cases;
while (cases--)
{
inputHandler();
cout << quantize(0, s) << endl;
}
return 0;
}
'Data Structures, Algorithm > 종만북' 카테고리의 다른 글
[종만북 문제] 삼각형 위의 최대 경로 수 세기 (문제 ID : TRIPATHCNT, 난이도 : 중) (0) | 2024.03.12 |
---|---|
[종만북 문제] 타일링 (문제 ID : TILING2, 난이도 : 하) (0) | 2024.03.12 |
[종만북 문제] 원주율 외우기 (문제 ID : PI, 난이도 : 하) (0) | 2024.03.10 |
[종만북 문제] 합친 LIS (문제 ID : JLIS, 난이도 : 하) (0) | 2024.03.10 |
[종만북 문제] 최대 증가 부분 수열 (문제 ID : LIS, 난이도 : 하) (0) | 2024.03.09 |
Comments