본문으로 바로가기

1699 제곱수의 합

category ps/다이나믹 프로그래밍 2021. 7. 18. 22:59

제곱수의 합 (1699)

 

풀이코드 (탐욕법 오답)

#include <iostream>
#include <cstdio>
#include <vector>
#include <cstring>
#include <cmath>
using namespace std;
// 1699 제곱수의 합

int n;

// input 의 제곱수들의 합의 최소개수를 반환한다.
int squared(int currentNum){
	// 기저 사례 : input 이 0이면 종료
	if(currentNum == 0)	return 0;
	
	int maxSqrt = (int)sqrt(currentNum);
	
	return squared(currentNum - pow(maxSqrt, 2)) + 1;
}

int main(){

	cin >> n;
	cin.ignore();

	int res = squared(n);
	
	cout << res << endl;
	
}

처음 이 문제에 접근할 때에 항상 최선의 방법을 취하는 탐욕법으로 해결해보았으나 틀렸다. 반례를 찾아보던 중 98 이라는 반례 를 알게되었다. 탐욕법이 최선이 아니라는 것이었다.

#include <iostream>
#include <cstdio>
#include <vector>
#include <cstring>
#include <cmath>
using namespace std;
// 1699 제곱수의 합

const int MAX = 100000;
// cache 는 각 수의 제곱수 항의 최소 개수를 갖는다.
int cache[MAX + 1];
int max(int a, int b) {return a < b ? b : a;}
int min(int a, int b) {return a < b ? a : b;}

int main(){
	int n;
	cin >> n;
	cin.ignore();

	fill_n(cache, MAX + 1, 317);
	cache[1] = 1;
	for(int i = 2; i <= n; i++){
		if(sqrt(i) - (int)sqrt(i) == 0) {
			cache[i] = 1;
			continue;
		}
		for(int j = 1; j*j <= i; j++){
			cache[i] = min(cache[i], 1 + cache[i - j*j]);
		}
	}
	
	cout << cache[n] << endl;
	
}

해설


적용한 레시피 / 방법론 + 접근법

9.21 장의 반복적 동적 계획법이 있다.

반복적 동적 계획법과 재귀적 동적 계획법의 차이를 알고 장단점을 이해하자.

풀이

어떠한 수 k = 제곱수 + (k - 제곱수) 로 표현 될 수 밖에 없다. 그러므로, k 보다 작은 모든 제곱수에 대하여 적용해보고 그 중 가장 min 인 값을 취해보았다.

아쉬웠던 부분 / 오답 원인 / 개선해야 할 점

- 재귀적으로 구현해보지 못한 것이 아쉽다.

- 또 초기화를 잘못 시켜서 나온 오답이 있었다. dp 문제일 수록 초기화에 신경 써야한다.

- 처음 반복적 동적 계획법을 적용할 때에는 i 가 제곱수가 되었을 때 별다른 조치를 취하지 않았었는데, 오류를 발견하고는 인위적으로 조건문을 넣어서 처리하였다. 조건문 없이 제곱수 까지 처리할 수 있는 방법이 있었을텐데 아쉽다.

 

반응형

'ps > 다이나믹 프로그래밍' 카테고리의 다른 글

2225 합분해  (0) 2021.07.23
9461 파도반 수열  (0) 2021.07.18
2579 계단 오르기  (0) 2021.07.18
1912 연속합  (0) 2021.07.18
[algospot] 최대 증가 부분 수열 LIS  (0) 2021.07.18