문제 이해
- 어떤 자연수 N은 그보다 작거나 같은 제곱수들의 합으로 나타낼 수 있다.
- 그때 합을 구성하는 제곱수들의 개수를 항의 개수라고 한다.
- N의 항의 개수가 최소가 되도록하는 개수를 구하라.
<조건>
- 시간 제한: 2초
- 메모리 제한: 128MB
<입력>
N: 자연수 (1 ~ 100,000, 10^5)
<출력>
항의 최소 개수
문제 풀이
이전에 푼 문제가 DP가 불가능한 그리디 문제였어서 DP를 전혀 생각하지 못했다.
접근 방식에 대해 항상 고민하자.
이 문제는 DP로 풀 수 있는데,
'dp[a] = b를 a를 나타낼 수 있는 항의 최소 개수는 b개이다.' 라고 정의했다.
예시를 들면 쉽게 이해할 수 있는데
N = 3)
3 = 1^2 + 1^2 + 1^2
dp[3] = 3이 될것이다.
그러나
N = 4)
4 = 2^2
dp[4] = 1이 된다.
먼저 dp[3] = 3이다.
그러나 dp[4] = dp[3] + 1(1^1의 가짓수)보다 dp[0] + 1(2^2의 가짓수)가 더 최솟값을 가진다.
1^2 + 1^2 + 1^2 +1^2보다
2^2의 항의 개수가 적은 것이다.
이런 식으로
dp[i - 제곱수] + 1과 dp[i]을 비교하며 계속 dp 배열을 최솟값으로 갱신해주는 문제이다!
for(int i = 1; i <= n; i++){
for(int j = 1; j * j <= i; j++){
dp[i] = min(dp[i], dp[i-j*j] + 1);
}
}
전체 코드
#include <iostream>
#include <algorithm>
#include <vector>
#include <queue>
#include <cstring>
using namespace std;
#define INF 987654321
int dp[101010];
int main() {
ios_base::sync_with_stdio(false);
cin.tie(nullptr);
int n;
cin>>n;
for(int i = 1; i <= n; i++) dp[i] = i;
for(int i = 1; i <= n; i++){
for(int j = 1; j * j <= i; j++){
dp[i] = min(dp[i], dp[i-j*j] + 1);
}
}
cout<<dp[n];
return 0;
}
'알고리즘 별 문제 정리 > DP' 카테고리의 다른 글
[C++] 백준 7579: 앱 (4) | 2024.10.13 |
---|---|
[C++] 백준 12865: 평범한 배낭 (0) | 2024.10.04 |
[C++] 백준 2011: 암호코드 (0) | 2024.08.13 |
[C++] 백준 1915: 가장 큰 정사각형 (0) | 2024.08.01 |
[C++] 백준 10492: 팰린드롬? (0) | 2024.07.20 |