BOJ

[BOJ] 1699번 : 제곱수의 합 (C++)

hg_studio 2023. 4. 13. 19:26

https://www.acmicpc.net/problem/1699

 

1699번: 제곱수의 합

어떤 자연수 N은 그보다 작거나 같은 제곱수들의 합으로 나타낼 수 있다. 예를 들어 11=32+12+12(3개 항)이다. 이런 표현방법은 여러 가지가 될 수 있는데, 11의 경우 11=22+22+12+12+12(5개 항)도 가능하다

www.acmicpc.net

1 = 1*1

2 = 1*1 + 1*1

3 = 1*1 + 1*1 + 1*1

4 = 2*2

5 = 2*2 + 1*1

6 = 2*2 + 1*1 + 1*1

7 = 2*2 + 1*1 + 1*1 + 1*1

8 = 2*2 + 2*2

9 = 3*3

 

이렇게 나타낼 수 있다. 

그래서 

d[1] = 1

d[2] = d[2-1]+1 = 2

d[3] = d[3-1]+1 = 3

d[4] = d[4-4]+1 = 1

d[5] = d[5-4]+1 = 2

d[6] = d[6-4]+1 = 3

d[7] = d[7-4]+1 = 4

d[8] = d[8-4]+1 = 2

d[9] = d[9-9]+1 = 1

 

일반화해보자면, 현재 구하고자 하는 수가 i이고 i보다 작거나 같은 제곱수가 j라고 할 때

d[i] = d[i - j*j] + 1 이 된다.

+1이 되는 이유는 이미 j*j라는 수를 한 번 이용했기 때문이다.

i에서 j*j만큼 사용했기 때문에 i-j*j 라는 수를 제곱수들의 합으로 표현할 때 최소개수를 구해주면 된다. 

 

그래서 우리는 i보다 작은 제곱수를 찾아야한다. 

소수구하기 문제를 풀어봤다면 알겠지만 제곱수를 찾는 방법은 이중포문을 돌려야 하는데, 시간복잡도를 루트를 씌운 효과를 내게 할 수 있는 방법이 있다. 

이중포문에서  outer포문이 i, inner포문이 j 라고 할 때, j*j<=i 라고 조건을 주는 방법이다.

이렇게 i보다 작거나 같은 제곱수가 될 수 있는 모든 j를 찾아야 한다.

왜냐하면, i보다 작거나 같은 제곱수 중에서 가장 큰 j가 정답이 아닐 때가 있기 때문이다.

예를 들어, i=52라고 해보자.

52보다 작거나 같은 제곱수 중에서 가장 큰 수는 49이다.

d[52] = d[52-49]+1 = d[3]+1 = 4 가 된다.

그러나 d[52] = d[52-36]+1 = d[16]+1 = 2 가 최소개수가 된다.

 

#include <iostream>
#include <algorithm>
using namespace std;

int n;
int d[100005];

int main(){
    ios::sync_with_stdio(0);
    cin.tie(0);

    cin>>n;

    for(int i=1; i<=n; i++){
        d[i]=0x7fffffff; //아주 큰수로 초기화
        for(int j=1; j*j<=i; j++){
            d[i] = min(d[i], d[i-j*j]+1);
        }
    }

    cout<<d[n];
}

 

d[i]에는 지금 구하고자 하는 j보다 작은 j들을 검사하여 그 중 가장 작은 값을 넣어놓게 된다. 

그래서 현재까지 가장 최소개수가 들어있는 d[i]와 d[i-j*j]+1 중에서 가장 작은 값을 검사하게 된다.

 

 

회고)

처음에 무조건 i보다 작거나 같은 제곱수 중에서 가장 큰 j를 찾고 그게 정답인 줄 알았다. 

그래서 모든 j가 아닌 딱 하나의 j를 가지고 d[i-j*j]+1를 해주게 되어 틀렸다. 

그래도 일반화한 식이랑 j*j<=i를 이용하여 제곱수 구하기는 잘 구현했었다.

52와 같은 예시를 생각해냈으면, i보다 작거나 같은 모든 제곱수에 대해 검사를 하였을텐데 

문제에 나와있는 예제들로는 그렇게 생각할 수 없어서 거의 다 생각해냈지만 결국 풀지 못한 게 아쉽다. 

 

'BOJ' 카테고리의 다른 글

[BOJ] 1915번 : 가장 큰 정사각형 (C++)  (2) 2023.04.14
[BOJ] 9084번 : 동전 (C++)  (0) 2023.04.14
[BOJ] 9251번 : LCS (C++)  (0) 2023.04.13
[BOJ] 2240번 : 자두나무 (C++)  (0) 2023.04.13
[BOJ] 10844번 : 쉬운 계단 수 (C++)  (0) 2023.04.12