문제
어떤 자연수 N은 그보다 작거나 같은 제곱수들의 합으로 나타낼 수 있다. 예를 들어 11=32+12+12(3개 항)이다. 이런 표현방법은 여러 가지가 될 수 있는데, 11의 경우 11=22+22+12+12+12(5개 항)도 가능하다. 이 경우, 수학자 숌크라테스는 “11은 3개 항의 제곱수 합으로 표현할 수 있다.”라고 말한다. 또한 11은 그보다 적은 항의 제곱수 합으로 표현할 수 없으므로, 11을 그 합으로써 표현할 수 있는 제곱수 항의 최소 개수는 3이다.
주어진 자연수 N을 이렇게 제곱수들의 합으로 표현할 때에 그 항의 최소개수를 구하는 프로그램을 작성하시오.
입력
첫째 줄에 자연수 N이 주어진다. (1 ≤ N ≤ 100,000)
출력
주어진 자연수를 제곱수의 합으로 나타낼 때에 그 제곱수 항의 최소 개수를 출력한다.
Solved.ac 레벨
실버 II
풀이
#include <bits/stdc++.h>
#define MAX 100001
using namespace std;
int n;
int dp[MAX];
int main(){
ios::sync_with_stdio(false);
cin.tie(NULL); cout.tie(NULL);
cin >> n;
for(int i = 1; i <= n; i++){
dp[i] = i;
}
for(int i = 2; i <= n; i++){
for(int j = 2; j*j <= i; j++){
dp[i] = min(dp[i], dp[i-j*j] + 1);
}
}
cout << dp[n] << '\n';
return 0;
}
dp 배열에 들어갈 값은 해당 숫자를 제곱수의 합으로 나타냈을 때 제곱수의 개수의 최소값이다. 수기로 작성하며 나열해보니...
dp[1] = 1
dp[2] = 2
dp[3] = 3
dp[4] = 1
dp[5] = 2
dp[6] = 3
dp[7] = 4
dp[8] = 2
.
.
.
별다른 규칙을 찾기가 힘들었다.
하지만 최소값만 가지고 생각하다보니 모든 숫자 n은 1^2를 n개만큼 더하면 제곱수의 개수를 최대로 할 수 있다. 따라서 dp 배열의 모든 원소는 해당 원소의 인덱스 i로 초기화시켜주면 된다.
for(int i = 1; i <= n; i++){
dp[i] = i;
}
그리고 이중 for문 (i, j)을 돌리며 dp 배열을 탐색하며 캐시를 이용해 최소값을 새로이 저장하게 된다. dp[4] 즉, n이 4인 경우로 예를 들어 보자.
dp[4]는 dp 배열 탐색 전 4로 초기화 되어있다.
4 = 1^2 + 1^2 + 1^2 + 1^2
이 dp[4]를 j = 2부터 올라오면서 j의 제곱이 4보다 작을때까지 반복한다
처음에는 j^2를 활용할 줄 몰라 out of bounds 오류를 맛봐야 했다. 하지만 j^2는 8과 9에도 적용이 된다는 것을 깨달았다.
for(int i = 2; i <= n; i++){ //i = 4
for(int j = 2; j*j <= i; j++){
// j = 2 * 2 = 4
dp[i] = min(dp[i], dp[i-j*j] + 1);
//dp[4] = min(dp[4], dp[4 - 4] + 1
//dp[4] = min(4, 1)
//dp[4] = 1
}
}
어쨌든 실버 2 치고는 수학적으로 생각할 필요가 있어서 점화식이 쉽게 나오지 않아 어려웠다.
'Study (etc) > Problem Solving' 카테고리의 다른 글
[BOJ / C++] 11055번 : 가장 큰 증가 부분 수열 (0) | 2022.09.02 |
---|---|
[BOJ / C++] 2170번 : 선 긋기 (0) | 2022.09.01 |
[BOJ / C++] 15988번 : 1, 2, 3 더하기 3 (0) | 2022.08.30 |
[BOJ / C++] 11000번 : 강의실 배정 (0) | 2022.08.29 |
[BOJ / C++] 11053번 : 가장 긴 증가하는 부분 수열 (0) | 2022.08.28 |