2629번: 양팔저울
첫째 줄에는 추의 개수가 자연수로 주어진다. 추의 개수는 30 이하이다. 둘째 줄에는 추의 무게들이 자연수로 가벼운 것부터 차례로 주어진다. 같은 무게의 추가 여러 개 있을 수도 있다. 추의 무
www.acmicpc.net
문제
양팔 저울과 몇 개의 추가 주어졌을 때, 이를 이용하여 입력으로 주어진 구슬의 무게를 확인할 수 있는지를 결정하려고 한다.
무게가 각각 1g과 4g인 두 개의 추가 있을 경우, 주어진 구슬과 1g 추 하나를 양팔 저울의 양쪽에 각각 올려놓아 수평을 이루면 구슬의 무게는 1g이다. 또 다른 구슬이 4g인지를 확인하려면 1g 추 대신 4g 추를 올려놓으면 된다.
구슬이 3g인 경우 아래 <그림 1>과 같이 구슬과 추를 올려놓으면 양팔 저울이 수평을 이루게 된다.
따라서 각각 1g과 4g인 추가 하나씩 있을 경우 주어진 구슬이 3g인지도 확인해 볼 수 있다.
<그림 2>와 같은 방법을 사용하면 구슬이 5g인지도 확인할 수 있다. 구슬이 2g이면 주어진 추를 가지고는 확인할 수 없다.
추들의 무게와 확인할 구슬들의 무게가 입력되었을 때, 주어진 추만을 사용하여 구슬의 무게를 확인 할 수 있는지를 결정하는 프로그램을 작성하시오.
입력
첫째 줄에는 추의 개수가 자연수로 주어진다. 추의 개수는 30 이하이다.
둘째 줄에는 추의 무게들이 자연수로 가벼운 것부터 차례로 주어진다.
같은 무게의 추가 여러 개 있을 수도 있다.
추의 무게는 500g이하이며, 입력되는 무게들 사이에는 빈칸이 하나씩 있다.
세 번째 줄에는 무게를 확인하고자 하는 구슬들의 개수가 주어진다. 확인할 구슬의 개수는 7이하이다.
네 번째 줄에는 확인하고자 하는 구슬들의 무게가 자연수로 주어지며, 입력되는 무게들 사이에는 빈 칸이 하나씩 있다.
확인하고자 하는 구슬의 무게는 40,000보다 작거나 같은 자연수이다.
출력
주어진 각 구슬의 무게에 대하여 확인이 가능하면 Y, 아니면 N 을 차례로 출력한다.
출력은 한 개의 줄로 이루어지며, 각 구슬에 대한 답 사이에는 빈칸을 하나씩 둔다.
알고리즘 분류
- 다이나믹 프로그래밍
- 배낭 문제
해설
이번 문제는 배낭 문제의 응용 문제이다.
이번 문제의 핵심은 3가지의 경우를 구하는 것이다.
- 현재 추를 저울에 올릴 것인가.
- 현재 추를 왼쪽에 올릴 것인가.
- 현재 추를 오른쪽에 올릴 것인가.
이 경우를 브루트 포스 알고리즘을 이용한다면 O(3^N)이 나올 것이다.
즉, DP를 이용하여 해결해야 한다.
위의 3가지 경우를 해결하기 위해 배낭 문제와 유사한 2차원 배열을 만들어 주었다.
dp[추의 개수][40001]
그 후 재귀를 이용했다.
private fun dp(cnt: Int, size: Int, num: Int){
if(dp[cnt][num]) return
dp[cnt][num] = true
if(cnt == size) return
dp(cnt + 1, size, num + balls[cnt])
dp(cnt + 1, size, num)
dp(cnt + 1, size, abs(num - balls[cnt]))
}
여기서 중복을 피하기 위하여 이미 true
인 값은 넘어가 주었다.
num + balls[cnt]
: 현재 저울(왼쪽)에 추를 추가한다.num
: 추를 저울에 추가하지 않는다.abs(num - balls[cnt])
: 현재 추를 오른쪽 저울에 추가한다.
여기서 절댓값을 넣어야 한다.
만약 왼쪽 저울에 2, 오른쪽 저울에 3의 값이 들어간다면 1의 크기를 만들 수 있고,
왼쪽 저울에 3, 오른쪽 저울에 2가 들어가도 1의 크기를 만들 수 있다.
다만 위의 경우에서는 -1의 값이 만들어지기에 IndexOutOfBounds
오류가 발생한다.
전체 코드
import java.io.BufferedReader
import java.io.InputStreamReader
import java.lang.Math.abs
private lateinit var dp: Array<Array<Boolean>>
private lateinit var balls: List<Int>
fun main() {
val br = BufferedReader(InputStreamReader(System.`in`))
val ballCnt = br.readLine().toInt()
balls = br.readLine().split(" ").map { it.toInt() }
val cnt = br.readLine().toInt()
val inputs = br.readLine().split(" ").map { it.toInt() }
dp = Array(ballCnt + 1) { Array(40001) { false } }
dp(0, ballCnt, 0)
inputs.forEach {
if(dp[ballCnt][it]){
print("Y ")
}else{
print("N ")
}
}
}
private fun dp(cnt: Int, size: Int, num: Int){
if(dp[cnt][num]) return
dp[cnt][num] = true
if(cnt == size) return
dp(cnt + 1, size, num + balls[cnt])
dp(cnt + 1, size, num)
dp(cnt + 1, size, abs(num - balls[cnt]))
}
정답
이번 문제는 열심히 고민해 봤으나,,,, 혼자 해결하지 못하고 다른 사람의 코드를 보고 해결했다.
다만 정답 코드를 보고나니 너무 간단한 문제여서 당황했다...
오히려 너무 어렵게 생각해서 쉽게 못풀었던 것 같다...
for문을 이용한 dp에만 집착해서 너무 복잡하게 생각했다.
다양한 방식으로 dp를 사용할 수 있다는 것을 느낄 수 있는 문제였따....🥺
'안찌의 개발일기 > Algorithm' 카테고리의 다른 글
프로그래머스 - 합승 택시 요금 (Kotlin) (0) | 2024.04.15 |
---|---|
백준 15662 - 톱니바퀴 (2) (Kotlin) (0) | 2024.04.15 |
백준 9252 - LCS 2 (Kotlin) (0) | 2024.04.11 |
백준 9251 - LCS (Kotlin) (1) | 2024.04.08 |
백준 12865 - 평범한 배낭 (Kotlin) (0) | 2024.04.08 |