해결 방법
이 문제의 핵심은 약수의 개수를 구하는 데 있다. 특정 숫자의 약수 개수를 알아야 등장 빈도를 알 수 있기 때문이다. 예를 들어서 숫자 4 를 예시로 들어보겠다. 억억단 예시 사진을 보면 해당 숫자가 세 번 등장하는데 그 이유가 구구단, 1단부터 e 단까지를 행렬로 표현해서 1X4 4X1 2X2 이렇게 나오기 때문이다.
그럼 여기서 대충 약수와 관련이 있다는 것을 눈치챌 수 있다. 4 의 약수는 총 세 개, 1, 2, 4 이다. 가운데를 기준으로 특정 위치에 있는 숫자는 자신의 반대쪽에 있는 숫자와 곱을 하면 4 가 된다. 그래서 원래는 4 가 두 개가 나오는 것이 맞지만 억억단은 행과 열 둘 다 구구단이 존재하기 때문에 동일 숫자 곱(2X2)을 제외하고 각각 2배를 해줘야 한다. 따라서 1, 4 는 1X4 4X1 이렇게 두 가지가 된다.
이런 특징을 이용해서 숫자 e 까지의 등장 빈도를 배열에 저장하고 이 배열을 이용해 result 를 구하면 되는데 문제는 등장 빈도를 구하는 속도가 빨라야 한다. 나같은 경우에는 처음에 제곱근을 이용한 약수 개수를 구하는 알고리즘을 이용해서 시도했었는데 시간 초과가 됐었다. 그래서 배수를 이용한 약수 개수 구하는 알고리즘으로 변경하여 제출했더니 통과가 됐다. 따라서 이 문제는 약수 개수를 구하는 알고리즘만 잘 짜면 그 뒤는 무난하게 풀 수 있다. 해결 순서는 다음과 같다.
e까지 각 숫자의 등장 빈도를 배열에 저장한다.1 2 3 4 5
for (i in 1..e) { for (j in 1..e / i) { counter[i*j]++ } }
starts배열 내 원소들을 하나씩 방문하여result를 구한다.getResult는i~j사이에서 빈도가 가장 높은 수와 빈도 값을 배열에 담아 리턴하는 메서드이다.1 2 3 4 5 6 7 8 9 10 11
private fun getResult(i: Int, j: Int, counter: Array<Int>): IntArray { var maxCnt = 0 var numOfMaxCnt = 0 for(num in i .. j) { if(counter[num] > maxCnt) { maxCnt = counter[num] numOfMaxCnt = num } } return intArrayOf(numOfMaxCnt, maxCnt) }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
var prev = getResult(sortedStarts.last()[0], e, counter) answer[sortedStarts.last()[1]] = prev[0] for(i in sortedStarts.lastIndex-1 downTo 0) { val curr = getResult(sortedStarts[i][0], sortedStarts[i + 1][0], counter) answer[sortedStarts[i][1]] = if (curr[1] >= prev[1]) curr[0] else prev[0] if(curr[1] > prev[1]) { answer[sortedStarts[i][1]] = curr[0] prev = curr } else if(curr[1] == prev[1]) { answer[sortedStarts[i][1]] = min(curr[0], prev[0]) prev = if(curr[0] < prev[0]) curr else prev } else { answer[sortedStarts[i][1]] = prev[0] } } answer.toIntArray()
설명
순서 1.
이 부분은 위에서 설명했듯이 배수를 이용한 약수 개수 구하는 알고리즘을 사용해서
e까지 빈도를 배열에 저장하면 된다.순서 2.
이 부분은 풀이 방법이 사람마다 다를 수 있는데 나같은 경우에는
starts를 정렬해놓고 내림차순으로 각 원소의result를 구하는 방법을 사용했다. 예를 들어서 문제 예시에서 준starts는[1, 3, 7]인데 이를 먼저 정렬한다. (근데 이미 정렬이 되어 있으므로 패스) 그리고e는8이다.이제
starts를 내림차순으로 반복문을 돌려서7~8->3~8->1~8이런 식으로result를 구한다. 그런데7~8을 먼저 계산을 하면3~8을 계산할 때3~7까지만 계산하면 된다.7~8부분은 이미 앞서 계산을 끝마쳤기 때문이다. 서로 두 결과값을 비교해서 빈도가 더 많은쪽을 설정해주면 된다. 만약 빈도가 서로 같다면 문제에서 내건 조건대로 숫자가 더 작은쪽을 설정해주면 된다. 이런 방식으로 계산을 하면은 마지막인1~8계산은1~3까지만 계산하고 앞서 계산한3~8값과 비교해서 값을 업데이트하면 된다.전체 코드
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60
import kotlin.math.* class Solution { fun solution(e: Int, starts: IntArray): IntArray { val answer = Array(starts.size) {0} val counter = Array(e+1) {0} val sortedStarts = Array(starts.size) { Array(2) {0} } for((i, num) in starts.withIndex()) { sortedStarts[i][0] = num sortedStarts[i][1] = i } sortedStarts.sortBy {it[0]} for (i in 1..e) { for (j in 1..e / i) { counter[i*j]++ } } return if(starts.size < 2) { intArrayOf(getResult(starts[0], e, counter)[0]) } else { var prev = getResult(sortedStarts.last()[0], e, counter) answer[sortedStarts.last()[1]] = prev[0] for(i in sortedStarts.lastIndex-1 downTo 0) { val curr = getResult(sortedStarts[i][0], sortedStarts[i + 1][0], counter) answer[sortedStarts[i][1]] = if (curr[1] >= prev[1]) curr[0] else prev[0] if(curr[1] > prev[1]) { answer[sortedStarts[i][1]] = curr[0] prev = curr } else if(curr[1] == prev[1]) { answer[sortedStarts[i][1]] = min(curr[0], prev[0]) prev = if(curr[0] < prev[0]) curr else prev } else { answer[sortedStarts[i][1]] = prev[0] } } answer.toIntArray() } } private fun getResult(i: Int, j: Int, counter: Array<Int>): IntArray { var maxCnt = 0 var numOfMaxCnt = 0 for(num in i .. j) { if(counter[num] > maxCnt) { maxCnt = counter[num] numOfMaxCnt = num } } return intArrayOf(numOfMaxCnt, maxCnt) } }