본문 바로가기

코드스테이츠 SEB FE 41기/데일리 코딩(Hard)

[알고리즘/javascript]orderOfPresentation

반응형

문제

말썽꾸러기 김코딩은 오늘도 장난을 치다가 조별 발표 순서가 담긴 통을 쏟고 말았습니다.

선생님께서는 미리 모든 발표 순서의 경우의 수를 저장해 놓았지만 김코딩의 버릇을 고치기 위해 문제를 내겠다고 말씀하셨습니다.

김코딩은 모든 조별 발표 순서에 대한 경우의 수를 차례대로 구한 뒤 발표 순서를 말하면 이 발표 순서가 몇 번째 경우의 수인지를 대답해야 합니다.

총 조의 수 N과 선생님이 말씀하시는 발표 순서 k가 주어질 때, 김코딩이 정답을 말 할 수 있게 올바른 리턴 값을 구하세요.

모든 경우의 수가 담긴 배열은 번호가 작을수록 앞에 위치한다고 가정합니다. ex) N = 3일경우, [[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]

입력

인자 1: N

  • Number 타입의 1 <= N <= 12인 조의 개수

인자 2: K

  • Number타입의 Array (0 <= index)

ex) n이 3이고 k가 [2, 3, 1]일 경우

모든 경우의 수를 2차원 배열에 담는다면 [[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]이 되고,

반환하는 값은 3이 됩니다.

주의사항

  • k내에 중복되는 요소는 없다고 가정합니다.

입출력 예시

let output = orderOfPresentation(3, [2, 3, 1]);
console.log(output); // 3

output = orderOfPresentation(5, [1, 3, 2, 4, 5])
console.log(output); // 6

 

코드

function orderOfPresentation (N, K) {
  // TODO: 여기에 코드를 작성합니다.
  // 조의 개수 N, 발표 순서 K

  // N은 최대 12이다.
  // 발표 순서를 만드는 것은 순열(permutation)이므로, 발표 순서의 최대 크기는 12!이다.
  // 이는 약 4억 8천만에 해당하며, 일반적인 수행 속도 상한(약 1억)을 훨씬 상회하므로 순열을 전부 생성하는 것은 올바른 접근 방법이 아니다.

  const factorial = (n) => {
    if (n <= 1) return 1;
    return n * factorial(n - 1);
  };

  // 발표 순서를 담는 변수 생성
  let order = 0;
  
  // N개의 조 중에, 어떠한 조가 이미 포함되었는지 확인하기 위해 배열을 생성한다.
  // 만약 N이 3이라면 [false, false, false, false]로 생성된다.
  // 제일 첫 번째는 더미 데이터이다. (인덱스는 0부터 시작하지만 조는 1부터 시작하기 때문에)
  const isUsed = Array(N + 1).fill(false);
  
  // K의 길이만큼 순회한다. 앞 자릿수부터 하나씩 줄여나가며 경우의 수 누적.
  // 2__ => 23_ => 231
  for (let i = 0; i < K.length; i++) {
    // K의 i번째 조를 변수에 담는다.
    const num = K[i];
    // 사용했는지 판별하기 위해 isUsed에 체크한다. (중복이 아니기 때문에)
    isUsed[num] = true;
    // num보다 앞에 올 수 있는 수들의 배열을 복제해서,
    const candidates = isUsed.slice(1, num);
    // 이 중에서 아직 사용되지 않은 수의 개수를 구한다.
    const validCnt = candidates.filter((el) => el === false).length;
    // 아직 사용되지 않은 수, 그 전까지의 모든 경우의 수를 카운트한다.
    // factorial(N - i - 1) => 아직 덜 채워진 자릿수에 대한 경우의 수 총 합
    const formerCnt = validCnt * factorial(N - i - 1);
    // order에 추가한다.
    // order => 그 전까지의 모든 경우의 수 갯수. 즉, 현재 앞에서 몇 번째 순서인지(순서는 0부터 시작)
    order = order + formerCnt;

    /**
     * 만약 K가 [2, 3, 1]이라고 가정했을 때, 첫 번째 num은 2가 될 것이다.
     * 2가 제일 앞에 있다고 가정한다면, 앞자리가 2의 차례가 오기 전에 1의 모든 경우의 수를 구했을 것이고,
     * 1의 모든 경우의 수를 지금부터 구하게 된다.
     * 
     * 그렇다면, IsUsed 배열은 이렇게 된다. [false(더미), false, true, false]
     * candidates 배열은 이렇게 된다. => [false]
     * validCnt는 이렇게 된다. => 1
     * formerCnt는 이렇게 된다. => 1 * factorial(3 - 0 - 1) 
     * // i는 0부터 시작하기 때문에 N에서 남아 있는 수를 구할 때 - 1이 추가로 필요하다.
     * order는 2를 추가한다.
     * 
     * 두 번째를 순회했을 땐, num이 3이 된다.
     * 그렇다면, IsUsed 배열은 이렇게 된다. [false(더미), false, true, true]
     * candidates 배열은 이렇게 된다. => [false]
     * validCnt는 이렇게 된다 => 1
     * formerCnt는 이렇게 된다 => 1 * factorial(3 - 1 - 1)
     * order는 1을 추가한다. (3)
     * 
     * 세 번째를 순회했을 땐, num이 1이 된다.
     * IsUsed 배열은 이렇게 된다. [false, true, true, true]
     * candidates 배열은 []이고, validCnt는 0이 되어, formerCnt는 0이 된다.
     * 
     * 발표 순서는 0부터 시작하기 때문에 0, 1, 2, 3으로
     * 결과적으로, 값은 3이 된다.
     */
  }
  
  return order;

}
반응형