[알고리즘/Swift] 3687 성냥개비
문제 정보
풀이
해당 문제는 min_dp 100개의 배열을 사용해서 해당 인덱스만큼의 성냥개비 개수로 만들 수 있는 최솟값을 배열에 넣었다.
이후, 성냥개비 개수가 주어지면 최솟값과 최댓값을 찾아야 하는데, 최댓값은 다음과 같은 알고리즘으로 쉽게 풀 수 있었다.
[ 최댓값 구하기 ]
성냥개비 단 두개로 숫자 1을 만들어서 자릿수를 높여야 한다.
그러므로 만약 성냥개비 개수가 짝수일 경우, 성냥개비 숫자 / 2 한 만큼 1을 붙여주면 된다.
ex) 성냥개비 개수 = 8개일 경우 -> 최댓값 1111
성냥개비 개수가 홀수일 경우, 1로 모든 성냥개비를 사용해서 채울 수 없으므로 가장 적은 개수로 만들 수 있는 가장 큰 수인 7을 앞에 붙여주고 남은 성냥개비로 1을 붙여준다
ex) 성냥개비 개수 = 7개일 경우 -> 최댓값 711
[ 최솟값 구하기 ]
최솟값 구하기는 아까 말했듯이 dp를 이용하여 풀었다.
위와 같이 규칙을 찾기 위해 계속 표를 작성해보았는데, 2 ~ 8까지는 직접 개수를 세서 적어놨고, 9부터는 점화식을 이용하여 dp 이전 원소들과 2 ~ 8 개수를 사용하여
최솟값을 구할 수 있었다.
예를 들어, 성냥개비 10개로 만들 수 있는 최솟값을 구하려면 다음과 같은 알고리즘을 거쳐야한다.
- 8개로 만들 수 있는 최솟값 10, min_dp[2] 배열 값 1을 나열 -> 101
-> 가장 처음에 보는 경우가 대부분 최댓값이므로, min값 갱신 초기화값으로 넣어준다 - 7개로 만들 수 있는 최솟값 8, min_dp[3] 배열 값 7을 나열 -> 87
- 6로 만들 수 있는 최솟값 6, min_dp[4] 배열 값 4을 나열 -> 64
- 5개로 만들 수 있는 최솟값 2, min_dp[5] 배열 값 2을 나열 -> 22
- 4개로 만들 수 있는 최솟값 4, min_dp[6] 배열 값 6을 나열 -> 46
- 3개로 만들 수 있는 최솟값 7, min_dp[7] 배열 값 8을 나열 -> 78
이것을 코드화하면 다음과 같다
// 9부터 점화식 적용
for i in 9...100 {
// 2번째 전 원소로 만든 숫자 뒤에 숫자 1을 붙인다 ex: i == 9 일 때, dp[7]*10 + dp[2] = 81
// 초기값으로 가장 클 것 같은 수를 넣어주고, 계속 비교해본다
dp[i] = dp[i-2]*10 + minNum[2]
for j in 3...7 {
// 더해서 i를 만들 수 있는 조합으로 비교하여 최솟값을 찾는다
dp[i] = min(dp[i], dp[i-j]*10 + minNum[j])
}
}
코드
import Foundation
// 최솟값: 3을 입력받으면 dp 배열 앞부터 돌면서 3인 값 출력
// 2 ~ 8 까지의 수는 미리 minNum 배열에 넣어놓는다. -> 점화식에서 일의 자리 수로 사용
var minNum: [Int] = [0, 0, 1, 7, 4, 2, 0, 8, 10]
// 해당 idx만큼의 성냥개비가 있을 때 만들 수 있는 최솟값을 넣어준다
var dp: [Int] = Array(repeating: 0, count: 101)
func findMin() {
// 2 ~ 8 까지는 minNum 값이 최솟값임
for i in 2...8 {
dp[i] = minNum[i]
}
// 숫자는 0으로 시작할 수 없다는 조건이 없으므로 6개일 때 만들 수 있는 최솟값은 0이 아니라 그 다음 작은 수 6이 되어야 한다
dp[6] = 6
// 9부터 점화식 적용
for i in 9...100 {
// 2번째 전 원소로 만든 숫자 뒤에 숫자 1을 붙인다 ex: i == 9 일 때, dp[7]*10 + dp[2] = 81
// 초기값으로 가장 클 것 같은 수를 넣어주고, 계속 비교해본다
dp[i] = dp[i-2]*10 + minNum[2]
for j in 3...7 {
// 더해서 i를 만들 수 있는 조합으로 비교하여 최솟값을 찾는다
dp[i] = min(dp[i], dp[i-j]*10 + minNum[j])
}
}
}
// 최댓값: 홀수인 경우 -> 3개로 7 구성하여 맨 앞자리 만들고 나머지 2개로 1 구성하여 채운다 ex: 7 -> 711
// 짝수인 경우 -> 2개로 나눈 값을 1로 붙인다
func findMax(_ num: Int) {
var result = Array(repeating: 1, count: num / 2)
if num % 2 != 0 {
result[0] = 7
}
print(result.map{String($0)}.joined(separator: ""))
}
findMin()
for _ in 1...Int(readLine()!)! {
let N = Int(readLine()!)!
// 예외 케이스 출력
if N == 2 {
print(1, 1, separator: " ")
}
else if N == 3 {
print(7, 7, separator: " ")
}
else {
print(dp[N], terminator: " ")
findMax(N)
}
}