[BAEKJOON] 1415번 사탕

2024. 3. 20. 21:24·Algorithm/Dynamic Programming

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

 

1415번: 사탕

첫째 줄에 슈퍼에 있는 사탕의 개수 N이 주어진다. N은 50보다 작거나 같은 자연수이다. 둘째 줄부터 N개의 줄에 각 사탕의 가격이 주어진다. 사탕의 가격은 10,000보다 작거나 같은 음이 아닌 정수

www.acmicpc.net

문제조건
  1. 사탕의 개수 : n개
  2. 사탕 가격의 합이 소수
  3. 모양이 똑같은 방법은 사지 않는다.

제일 먼저 떠오른 생각은 집합이었다.

집합이 한번 나오면 모양이 똑같은 방법을 커버할 수 있고, 그 집합의 원소들의 합이 소수가 되면 되기 때문이다.

 

라고 생각했지만 도저히 이 방법으로는 풀리지 않을 것 같았다,,,

 

일단 로직에 대해서는 더 고민해보기로 하고 소수 구하는 알고리즘을 보겠다.

 

void isPrime() {
	prime[0] = prime[1] = false;
	//전부 소수라고 초기화
	for (int i = 2; i <= MAX; i++) {
		prime[i] = true;
	}
	//각 배수로 나뉘어지면 false로 바꿈
	for (int i = 2; i*i <= MAX; i++) {
		if (!prime[i]) continue;
		for (int j = i * i; j < MAX; j += i) {
			prime[j] = false;
		}
	}
}

숫자가 크면 클수록 판단하는데 for문을 두번돌면서 n^2인 시간복잡도가 매우 높아질 것이기 때문에, 이 복잡도를 낮추기 위해서, i*i까지만 for문이 돌도록 해줬다. n이하의 소수를 구하는데, √n까지만 구하고, 그 이후의 배수는 n을 넘어 가기때문에 계산과정 자체가 의미없기 때문이다.

위의 계산방법을 에라토스테네스의 체 라고 한다.

 

이렇게 에라토스테네스의 체를 구현하고 보니 어차피 판단은 prime 배열로 해주면 되고, 그러면 내가 구해야 할것은,  모든 경우의 수의 가격이다,,! DP로 풀란소리다,,,,,,

 

일단 DP로 접근해보기 위해서 점화식을 생각해봤다. 꽤 많이 ㅎㅎ,,

점심 나가서 먹을것 같다 ㅎㅎ

 

정말 까다로운 문제인게 우선 dp이므로 숫자가 엄청 커질것에 대한 대비를 못했다. int 범위가 대략 21만까지 인데 dp배열의 최대 길이만 50만이므로, long long으로 바꿔주었다.

dp[n] : 사탕 가격의 합이 n원일 때 주어진 사탕 가격에서의 모든 경우의 수
dp[i] = dp[i] + ∑ dp[i - 각각 사탕가격 * 사탕개수]) (1 <= 사탕개수 <= 총 사탕개수)

 

이를 구현하면 삼중 for문으로 머리가 터지는 줄 알았다,,,,,

단순하게 생각하면, n원 이하의 가격에서 모든 종류의 사탕을 개수별로 빼준것을 하나씩 더 해주면 n원에서의 모든 경우의 수가 top-down approach로 계산된다. 따라서 우리는 특정하게 값을 모르더라도 초반 dp값 하나만 입력해주면, 계산이 자동으로 된다는 것이다.

그리고 가격이 0인 사탕의 경우를 입력을 받을때부터 따로 카운트해서 0의 개수 + 1 만큼만 곱해줬다. 이러면 뭔가 로직이 깔끔하게 정리 된것 같다.

 

#include <iostream>
#include <vector>
#define MAX 500001
using namespace std;
bool prime[MAX];
int n;

//first : 사탕 가격, second : 같은 가격 사탕개수
vector<pair<int, int> > v;
//n원일 때 주어진 입력에서의 모든 경우의 수
long long dp[MAX], answer;


// n종류 사탕, 사탕 가격의 합 = 소수
void isPrime() {
	prime[0] = prime[1] = false;
	//전부 소수라고 초기화
	for (int i = 2; i < MAX; i++) {
		prime[i] = true;
	}
	//각 배수로 나뉘어지면 false로 바꿈
	for (int i = 2; i*i <= MAX; i++) {
		if (!prime[i]) continue;
		for (int j = i * i; j < MAX; j += i) {
			prime[j] = false;
		}
	}
}

int main(){
	cin >> n;
	int cnt = 1;
	for (int i = 0; i < n; i++) {
		int num = -1;
		int a;
		cin >> a;
		if (a == 0) {
			cnt++;
			continue;
		}
		for (int i = 0; i < v.size(); i++) {
			if (a == v[i].first) {
				num = i;
				break;
			}
		}
		if (num == -1) v.push_back({ a,1 });
		else v[num].second++;
	}
	//소수찾기 끝
	isPrime();
	dp[0] = 1;

	for (pair<int,int> a : v) {
		for (int i = MAX; i >= 0; i--) {
			for (int j = 1; j <= a.second; j++) {
				if (i - a.first * j < 0) break;
				dp[i] += dp[i - a.first * j];
			}
		}
	}

	for (int i = 2; i <= MAX; i++)
		if (prime[i])
			answer += dp[i];
	cout << answer * cnt;
}

진짜 어려웠다,,,

'Algorithm > Dynamic Programming' 카테고리의 다른 글

[BAEKJOON] 14501번 퇴사  (1) 2024.04.07
[BAEKJOON] 3114번 사과와 바나나  (0) 2024.04.05
[BAEKJOON] 9252번 LCS 2  (0) 2024.03.29
[BAEKJOON] 1509번 팰린드롬 분할  (0) 2024.03.29
[BAEKJOON] 2228번 구간 나누기  (0) 2024.03.24
'Algorithm/Dynamic Programming' 카테고리의 다른 글
  • [BAEKJOON] 3114번 사과와 바나나
  • [BAEKJOON] 9252번 LCS 2
  • [BAEKJOON] 1509번 팰린드롬 분할
  • [BAEKJOON] 2228번 구간 나누기
Ls._.Rain
Ls._.Rain
안되면 될때까지 삽질했던 기록
  • Ls._.Rain
    Ls{Diary}
    Ls._.Rain
  • 전체
    오늘
    어제
    • 분류 전체보기 (136)
      • Github (2)
      • Spring (51)
        • Batch Programming (13)
        • 결제 (4)
        • 대용량 트래픽 (32)
        • OpenAI (0)
        • Security (0)
        • WebSocket (0)
        • JPA (1)
      • Algorithm (67)
        • DFS (6)
        • BFS (6)
        • Dynamic Programming (10)
        • Brute Force (4)
        • Binary Search (6)
        • 구현, 시뮬레이션 (15)
        • Stack (1)
        • Greedy (4)
        • Priority_Queue (2)
        • Back Tracking (3)
        • Geometry (2)
        • SCC (1)
        • 투포인터 (4)
        • 최대유량 (1)
        • 정렬 (1)
      • OS (0)
      • DevOps (15)
        • AWS (11)
        • Docker (4)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • hELLO· Designed By정상우.v4.10.0
Ls._.Rain
[BAEKJOON] 1415번 사탕
상단으로

티스토리툴바