문제 내용
문제
N개의 정수로 이루어진 수열이 있을 때, 크기가 양수인 부분수열 중에서 그 수열의 원소를 다 더한 값이 S가 되는 경우의 수를 구하는 프로그램을 작성하시오.
입력
첫째 줄에 정수의 개수를 나타내는 N과 정수 S가 주어진다. (1 ≤ N ≤ 20, |S| ≤ 1,000,000) 둘째 줄에 N개의 정수가 빈 칸을 사이에 두고 주어진다. 주어지는 정수의 절댓값은 100,000을 넘지 않는다.
출력
첫째 줄에 합이 S가 되는 부분수열의 개수를 출력한다.
예제 입력 | 예제 출력 |
5 0 -7 -3 -2 5 8 |
1 |
문제 풀이
재귀로 문제를 풀었다. 수열을 만든다 = 조합을 만든다 라고 생각해서 풀었다. 조합을 만드는데, 조합의 원소 갯수는 제한이 없는거지.. 그냥 순서가 없이 뽑는 경우를 만들고, 조합을 뽑고 있는데 원소들의 합이 문제 조건에 주어지는 합과 같다면 count를 증가시켜주면 된다.
여기서 생각을 해보자, 만약에 1, 2, -3, -1, 1 이라는 리스트가 주어지고, 원하는 합은 0이다. 이때, 나올 수 있는 경우는 어떻게 될까?
이 경우를 주목해서, [1, 2, -3]으로 결과가 나왔다고 해서 종료를 시키지 않고, cnt를 증가시키고 return 시키지는 않고 계속해서 탐색할 수 있도록 하였다.
이부분은 실수를 한 부분인데, 문제에서 sum이 0으로 주어져서, 공집합을 제하기 위해 cnt를 -1로 두고 시작했다. 근데 sum이 무조건 0이라고 한적도 없고.. 0일때 말고는 sum 초깃값과 겹칠 일이 없다! 따라서, 출력을 할 때, s가 0이라면 cnt에서 1을 빼고 출력을 해주었다.
</>̆̈ 코드
private var n = 0
private var s = 0
private lateinit var array: Array<Int>
private var cnt = 0
fun main() {
val br = System.`in`.bufferedReader()
br.readLine().split(" ").run {
n = this.first().toInt(); s = this.last().toInt()
}
array = br.readLine().split(" ").map { it.toInt() }.toTypedArray()
sumOfPart()
print(if(s==0) cnt-1 else cnt)
}
private fun sumOfPart(index: Int = 0, depth: Int = 0, sum: Int = 0) {
if (sum == s) cnt++
if (depth == n) return
for (i in index until n) {
sumOfPart(i + 1, depth + 1, sum + array[i])
}
}
링크