본문 바로가기
알고리즘/프로그래머스

프로그래머스: 2019 카카오 블라인드 - 실패율

by 패쓰킴 2022. 8. 1.
728x90

https://school.programmers.co.kr/learn/courses/30/lessons/42889

문제 설명

실패율

failture_rate1.png

슈퍼 게임 개발자 오렐리는 큰 고민에 빠졌다. 그녀가 만든 프랜즈 오천성이 대성공을 거뒀지만, 요즘 신규 사용자의 수가 급감한 것이다. 원인은 신규 사용자와 기존 사용자 사이에 스테이지 차이가 너무 큰 것이 문제였다.

이 문제를 어떻게 할까 고민 한 그녀는 동적으로 게임 시간을 늘려서 난이도를 조절하기로 했다. 역시 슈퍼 개발자라 대부분의 로직은 쉽게 구현했지만, 실패율을 구하는 부분에서 위기에 빠지고 말았다. 오렐리를 위해 실패율을 구하는 코드를 완성하라.

  • 실패율은 다음과 같이 정의한다.
    • 스테이지에 도달했으나 아직 클리어하지 못한 플레이어의 수 / 스테이지에 도달한 플레이어 수

전체 스테이지의 개수 N, 게임을 이용하는 사용자가 현재 멈춰있는 스테이지의 번호가 담긴 배열 stages가 매개변수로 주어질 때, 실패율이 높은 스테이지부터 내림차순으로 스테이지의 번호가 담겨있는 배열을 return 하도록 solution 함수를 완성하라.

제한사항
  • 스테이지의 개수 N은 1 이상 500 이하의 자연수이다.
  • stages의 길이는 1 이상 200,000 이하이다.
  • stages에는
  1

이상

  N + 1

이하의 자연수가 담겨있다.

  • 각 자연수는 사용자가 현재 도전 중인 스테이지의 번호를 나타낸다.
  • 단, N + 1 은 마지막 스테이지(N 번째 스테이지) 까지 클리어 한 사용자를 나타낸다.
  • 만약 실패율이 같은 스테이지가 있다면 작은 번호의 스테이지가 먼저 오도록 하면 된다.
  • 스테이지에 도달한 유저가 없는 경우 해당 스테이지의 실패율은 0 으로 정의한다.
입출력 예
N stages result
5 [2, 1, 2, 6, 2, 4, 3, 3] [3,4,2,1,5]
4 [4,4,4,4,4] [4,1,2,3]
입출력 예 설명

입출력 예 #1
1번 스테이지에는 총 8명의 사용자가 도전했으며, 이 중 1명의 사용자가 아직 클리어하지 못했다. 따라서 1번 스테이지의 실패율은 다음과 같다.

  • 1 번 스테이지 실패율 : 1/8

2번 스테이지에는 총 7명의 사용자가 도전했으며, 이 중 3명의 사용자가 아직 클리어하지 못했다. 따라서 2번 스테이지의 실패율은 다음과 같다.

  • 2 번 스테이지 실패율 : 3/7

마찬가지로 나머지 스테이지의 실패율은 다음과 같다.

  • 3 번 스테이지 실패율 : 2/4
  • 4번 스테이지 실패율 : 1/2
  • 5번 스테이지 실패율 : 0/1

각 스테이지의 번호를 실패율의 내림차순으로 정렬하면 다음과 같다.

  • [3,4,2,1,5]

입출력 예 #2

모든 사용자가 마지막 스테이지에 있으므로 4번 스테이지의 실패율은 1이며 나머지 스테이지의 실패율은 0이다.

  • [4,1,2,3]

풀이

전체 스테이지 수(N)만큼만 return array 크기를 맞추면 되고,

  1. 1보다 크거나 같은 사용자는 총 8명, 1스테이지를 통과하지 못한 사용자 1명
  2. 2보다 크거나 같은 사용자는 총 7명, 2스테이지를 통과하지 못한 사용자 3명

이런식으로 실패율을 구하기위해 필요한 수를 구할 것이다.

각 사용자가 도전 중인 스테이지가 1...N.count 안에 있으면 해당 스테이지를 통과하지 못한 것이므로 fail에 통과하지 못한 사용자를 카운트 하고,

전체 사용자에서 통과하지 못한 사용자 만큼 빼준 후 통과하지 못한 사용자 수로 나누어준다.(=실패율)

실패율이 높은 순으로 return해주면 됨

코드

// try 1
func solution(_ N:Int, _ stages:[Int]) -> [Int] {
    var failPercent = [Int: Double]()

    for stage in 1...N {
        let player = stages.filter{stage <= $0}.count
        let fail = stages.filter{stage == $0}.count
        failPercent[stage] = Double(fail) / Double(player)
    }

    let result = failPercent.sorted(by: <).sorted(by: {$0.value > $1.value})
    return result.map{$0.key}
}

// try 2
func solution(_ N:Int, _ stages:[Int]) -> [Int] {
    var failPercent = [Int: Double]()
    var player = 0
    var fail = 0

    for stage in 1...N {
        for i in stages {
            if i >= stage {
                player += 1
            }
            if i == stage {
                fail += 1
            }
        }
        failPercent[stage] = Double(fail) / Double(player)
        player = 0
        fail = 0
    }

    let result = failPercent.sorted(by: <).sorted(by: {$0.value > $1.value})
    return result.map{$0.key}
}

// try 3
func solution(_ N:Int, _ stages:[Int]) -> [Int] {
    var failPercent = [(Int, Double)]()
    var player = 0
    var fail = 0

    for stage in 1...N {
        for i in stages {
            if i >= stage {
                player += 1
            }
            if i == stage {
                fail += 1
            }
        }
        failPercent.append((stage,Double(fail) / Double(player)))
        player = 0
        fail = 0
    }

    let result = failPercent.sorted(by: <).sorted(by: {$0.1 > $1.1})
    return result.map{$0.0}
}

// try 4
func solution(_ N:Int, _ stages:[Int]) -> [Int] {
    var failPercent = [(Int, Double)]()
    var player = stages.count

    for stage in 1...N {
        var fail = 0
        for i in stages {
            if i == stage {
                fail += 1
            }
        }
        player -= fail
        failPercent.append((stage, Double(fail) / Double(player)))
    }

    failPercent = failPercent.sorted(by: {$0.1 > $1.1 })
    return failPercent.map{$0.0}
}

실패이유

  1. 딕셔너리 보다 튜플의 속도가 빠르다
    이유, 튜플은 최대 크기가 정해져 있는 반면 딕셔너리는 사용자가 원하는 만큼 크기가 늘어날 수 있고
    확실한 이유가 될지는 모르겠으나 딕셔너리의 key가 heap 영역에 저장되는 반면 튜플은 stack에 저장된다. - (이 부분은 좀 더 정확한 공부 필요!)
  2. filter보다 for문을 이용하면 복잡도가 줄어든다

그래서 tuple과 for문을 이용해서 수정을 했으나 12번 문제에서 런타임 에러가 발생했다.

func solution(_ N:Int, _ stages:[Int]) -> [Int] {
    var failPercent = [(Int, Double)]()
    var player = stages.count

    for stage in 1...N {
        var fail = 0
        for i in 0 ..< stages.count {
            if stages[i] == stage {
                fail += 1
            }
        }
        player -= fail
        failPercent.append((stage, Double(fail) / Double(player)))
    }

    failPercent = failPercent.sorted(by: {$0.1 > $1.1 })
    return failPercent.map{$0.0}
}

두번째 for문에 sequence를 그냥 stage에서 0..<stages.count 로 변경하니 통과가 됐는데 이 이유는 잘 모르겠심! 공부해야겠음

쉬운듯 어려웠다...

정답은 https://rldd.tistory.com/322 여기 참고

자료형에 대해서는

https://coding-sojin2.tistory.com/3

https://devsrkim.tistory.com/entry/Swift-Array-Dictionary-Set-Tuple

참고

728x90

댓글