λ³Έλ¬Έ λ°”λ‘œκ°€κΈ°

Algorithm

[Algorithm] 숫자 νƒ€μž λŒ€νšŒ

πŸ“‹ 문제

μœ„μ™€ 같은 λͺ¨μ–‘μœΌλ‘œ λ°°μ—΄λœ 숫자 자판이 μžˆμŠ΅λ‹ˆλ‹€. 숫자 νƒ€μž λŒ€νšŒλŠ” 이 λ™μΌν•œ μžνŒμ„ μ‚¬μš©ν•˜μ—¬ 숫자둜만 이루어진 κΈ΄ λ¬Έμžμ—΄μ„ λˆ„κ°€ κ°€μž₯ λΉ λ₯΄κ²Œ νƒ€μ΄ν•‘ν•˜λŠ”μ§€ κ²¨λ£¨λŠ” λŒ€νšŒμž…λ‹ˆλ‹€.

λŒ€νšŒμ— μ°Έκ°€ν•˜λ €λŠ” λ―Όν¬λŠ” λ‘ μ—„μ§€ μ†κ°€λ½μ„ μ΄μš©ν•˜μ—¬ νƒ€μ΄ν•‘을 ν•©λ‹ˆλ‹€. λ―Όν¬λŠ” ν•­μƒ μ™Όμ† μ—„μ§€λ₯Ό 4 μœ„에, μ˜€λ₯Έμ† μ—„μ§€λ₯Ό 6 μœ„에 λ‘κ³  νƒ€μ΄ν•‘을 μ‹œμž‘ν•©λ‹ˆλ‹€. μ—„μ§€ μ†κ°€λ½μ„ μ›€μ§μ—¬ λ‹€μŒ μˆ«μžλ₯Ό λˆ„λ₯΄λŠ” λ°μ—λŠ” μΌμ • μ‹œκ°„이 λ“­λ‹ˆλ‹€. λ―Όν¬λŠ” μ–΄λ–€ λ‘ μˆ«μžλ₯Ό μ—°μ†μœΌλ‘œ μž…λ ₯ν•˜λŠ” μ‹œκ°„ λΉ„μš©μ„ λͺ‡λͺ‡ κ°€μ€‘μΉ˜λ‘œ λΆ„λ₯˜ν•˜μ˜€μŠ΅λ‹ˆλ‹€.

 

  • μ΄λ™ν•˜μ§€ μ•Šκ³  μ œμžλ¦¬μ—μ„œ λ‹€μ‹œ λˆ„λ₯΄λŠ” 것은 κ°€μ€‘μΉ˜κ°€ 1μž…λ‹ˆλ‹€.
  • μƒν•˜μ’Œμš°λ‘œ μΈμ ‘ν•œ μˆ«μžλ‘œ μ΄λ™ν•˜μ—¬ λˆ„λ₯΄λŠ” κ²ƒμ€ κ°€μ€‘μΉ˜κ°€ 2μž…λ‹ˆλ‹€.
  • λŒ€κ°μ„ μœΌλ‘œ μΈμ ‘ν•œ μˆ«μžλ‘œ μ΄λ™ν•˜μ—¬ λˆ„λ₯΄λŠ” κ²ƒμ€ κ°€μ€‘μΉ˜κ°€ 3μž…λ‹ˆλ‹€.
  • κ°™μ§€ μ•Šκ³  μΈμ ‘ν•˜μ§€ μ•Šμ€ μˆ«μžλ₯Ό λˆ„λ₯Ό λ•ŒλŠ” μœ„ κ·œμΉ™μ— λ”°λΌ κ°€μ€‘μΉ˜ ν•©μ΄ μ΅œμ†Œκ°€ λ˜λŠ” κ²½λ‘œλ₯Ό λ”°λ¦…λ‹ˆλ‹€.

예λ₯Ό λ“€μ–΄ 1 μœ„μ— 있던 손가락을 0 으둜 μ΄λ™ν•˜μ—¬ λˆ„λ₯΄λŠ” 것은 2 + 2 + 3 = 7 만큼의 κ°€μ€‘μΉ˜λ₯Ό κ°–μŠ΅λ‹ˆλ‹€.
단, μˆ«μž μžνŒμ€ λ²„νŠΌμ˜ ν¬κΈ°κ°€ μž‘κΈ° λ•Œλ¬Έμ— κ°™μ€ μˆ«μž λ²„νŠΌ μœ„에 λ™μ‹œμ— λ‘ μ—„μ§€ μ†κ°€λ½μ„ μ˜¬λ €λ†“을 μˆ˜ μ—†μŠ΅λ‹ˆλ‹€. μ¦‰, μ–΄λ–€ μˆ«μžλ₯Ό λˆŒλŸ¬μ•Ό ν•  μ°¨λ‘€μ— κ·Έ μˆ«μž μœ„에 μ˜¬λ €μ Έ μžˆλŠ” μ†κ°€λ½μ΄ μžˆλ‹€λ©΄ λ°˜λ“œμ‹œ κ·Έ μ†κ°€λ½μœΌλ‘œ λˆŒλŸ¬μ•Ό ν•©λ‹ˆλ‹€.

숫자둜 μ΄λ£¨μ–΄μ§„ λ¬Έμžμ—΄ numbersκ°€ μ£Όμ–΄μ‘Œμ„ λ•Œ μ΅œμ†Œν•œμ˜ μ‹œκ°„μœΌλ‘œ νƒ€μ΄ν•‘을 ν•˜λŠ” κ²½μš°μ˜ κ°€μ€‘μΉ˜ ν•©μ„ return ν•˜λ„둝 solution ν•¨μˆ˜λ₯Ό μ™„μ„±ν•΄μ£Όμ„Έμš”.

μ œν•œμ‚¬ν•­

  • 1 ≤ numbers의 길이 ≤ 100,000
    • numbersλŠ” μ•„라비아 μˆ«μžλ‘œλ§Œ μ΄λ£¨μ–΄μ§„ λ¬Έμžμ—΄μž…λ‹ˆλ‹€.

 

μž…μΆœλ ₯ μ˜ˆ

"1756" 10
"5123" 8


✏️ 첫번째 μ˜€λ‹΅μ„ 봀던 풀이 (μ •λ‹΅ν’€μ΄λŠ” μ•„λž˜!)

처음 μ‹œλ„ν–ˆλ˜ μž¬κ·€λ₯Ό μ΄μš©ν•œ μ ‘κ·Όλ°©λ²•μœΌλ‘œ μ˜€λ‹΅μ„Έλ‘€λ₯Ό κ²½ν—˜ν–ˆλ‹€. μŠ€ν…μ΄ λ„ˆλ¬΄ 많이 μŒ“μ—¬μ„œμΈμ§€ λŸ°νƒ€μž„μ—λŸ¬λ„ λ°œμƒν–ˆλ‹€.(κ·ΈλŸ΄λ§Œν•΄..?)

 

μš°μ„  처음으둜 boardλΌλŠ” λ³€μˆ˜λ₯Ό ν•˜λ‚˜ μ„ μ–Έν–ˆλ‹€. 이 boardμ—λŠ” 이차원배열을 ν• λ‹Ήν–ˆλŠ”λ° μ΄λŠ” 인덱슀λ₯Ό 번호둜 κ°€μ§€κ³  μ™ΈλΆ€ λ°°μ—΄μ˜ μš”μ†Œλ‘œλŠ” λ°°μ—΄λ‘œ μœ„μΉ˜λ₯Ό κ°€μ§„λ‹€. ex) 1μ΄λΌλŠ” μˆ«μžλŠ” κ°€λ‘œ 1번째, μ„Έλ‘œ 1번째 -> [1, 1]

 

κ·Έ λ‹€μŒ calcDistλΌλŠ” ν•¨μˆ˜λ₯Ό μž‘μ„±ν–ˆλ‹€. boardλ₯Ό λ°”νƒ•μœΌλ‘œ 번호 μ‚¬μ΄μ˜ (κ°€μ€‘μΉ˜)λ₯Ό κ΅¬ν–ˆλ‹€. 예λ₯Ό λ“€μ–΄ 1λ²ˆμ—μ„œ 3번으둜 갈 λ•ŒλŠ” 우방ν–₯으둜 2νšŒμ΄κΈ°λ•Œλ¬Έμ— κ°€μ€‘μΉ˜λŠ” 4κ°€ λœλ‹€. 이λ₯Ό 숫자의 μœ„μΉ˜λ₯Ό λ°”νƒ•μœΌλ‘œ κ³„μ‚°ν–ˆλ‹€. ν˜„μž¬ μˆ«μžμ™€ λͺ©ν‘œλ‘œ ν•˜λŠ” 숫자의 μœ„μΉ˜(board)λ₯Ό 거리둜 κ³„μ‚°ν–ˆλ‹€. 각각 μ„Έλ‘œ κ°€λ‘œμ˜ λ–¨μ–΄μ§„ 거리λ₯Ό κ³„μ‚°ν•˜κ³  두 수(κ°€λ‘œ, μ„Έλ‘œ)κ°€ λͺ¨λ‘ 0이상이면 λŒ€κ°μ„ μœΌλ‘œ 갈 수 있기 λ•Œλ¬Έμ— +3을 ν•΄μ£Όκ³ , λ‘˜ 쀑 ν•˜λ‚˜λ§Œ μ‘΄μž¬ν•  경우 μƒν•˜μ’Œμš°λ‘œ μ΄λ™ν•˜λŠ” κ²½μš°μ΄λ―€λ‘œ +2λ₯Ό ν•΄μ€€λ‹€.

 

이제 μž¬κ·€λ₯Ό λˆλ‹€. 왼손 였λ₯Έμ† 각각 λͺ©ν‘œλ‘œ ν•˜λŠ” μœ„μΉ˜λ‘œ μ΄λ™ν•˜μ—¬ κ°€μ€‘μΉ˜λ₯Ό κ³„μ‚°ν•˜λ„ λ§Œμ•½ κ°€μ€‘μΉ˜κ°€ λ™μΌν•˜λ‹€λ©΄ μž¬κ·€ν•¨μˆ˜λ₯Ό ν˜ΈμΆœν•˜κ³  κ·Έλ ‡μ§€ μ•ŠμœΌλ©΄ 더 μž‘μ€ 숫자λ₯Ό 더해주고 ν˜„μž¬ μ†μ˜ μœ„μΉ˜λ₯Ό λ³€κ²½μ‹œμΌœμ€€λ‹€. λ§ˆμ§€λ§‰ μˆ«μžκΉŒμ§€ 이 과정을 λ°˜λ³΅ν•˜λ©΄μ„œ answer에 Math.min을 μ΄μš©ν•˜μ—¬ μ΅œμ†Œκ°’μ„ 할당해쀬닀.

 

정닡을 잘 λ‚˜μ˜€λŠ” 것 κ°™μ§€λ§Œ λŸ°νƒ€μž„μ—λŸ¬! -> μ•„λ§ˆ μž¬κ·€λ‘œ μΈν•œ μŠ€ν…μ΄ λ„ˆλ¬΄ 많이 μŒ“μ—¬μ„œλ‘œ μ˜ˆμƒν•œλ‹€.

 

⌨️ 첫번째 μ˜€λ‹΅ μ½”λ“œ 

function solution(numbers) {
  var answer = Infinity;

  const board = [
    [4, 2],
    [1, 1],
    [1, 2],
    [1, 3],
    [2, 1],
    [2, 2],
    [2, 3],
    [3, 1],
    [3, 2],
    [3, 3],
  ];

  const calcDist = (now, target) => {
    if (now == target) return 1;

    const [nowX, nowY] = board[now];
    const [targetX, targetY] = board[target];

    let x = Math.abs(nowX - targetX);
    let y = Math.abs(nowY - targetY);

    let cnt = 0;
    while (x || y) {
      if (x && y) {
        cnt += 3;
        x -= 1;
        y -= 1;
      } else if (x) {
        cnt += 2 * x;
        x = 0;
      } else if (y) {
        cnt += 2 * y;
        y = 0;
      }
    }
    return cnt;
  };

  const calc = (pos, idx, w) => {
    for (idx; idx < numbers.length; idx++) {
      const temp1 = calcDist(pos[0], numbers[idx]);
      const temp2 = calcDist(pos[1], numbers[idx]);
      if (temp1 === temp2) {
        calc([numbers[idx], pos[1]], idx + 1, w + temp1);
        calc([pos[0], numbers[idx]], idx + 1, w + temp2);
        return;
      } else if (temp1 < temp2) {
        w += temp1;
        pos[0] = numbers[idx];
      } else {
        w += temp2;
        pos[1] = numbers[idx];
      }
    }

    answer = Math.min(w, answer);
    return;
  };

  calc([4, 6], 0, 0);

  return answer;
}

 

 

✏️ μ •λ‹΅ 풀이

μž¬κ·€λ₯Ό μ΄μš©ν•˜λ‹€κ°€ μ‹œκ°„μ΄ˆκ³Όκ°€ λ‚œλ‹€λ©΄ DPμ‹μœΌλ‘œ ν’€μ–΄μ•Όν•˜μ§€ μ•Šμ„κΉŒ? λΌλŠ” 생각을 ν–ˆλ‹€. ν•˜μ§€λ§Œ 왼손 였λ₯Έμ†μ˜ μœ„μΉ˜λ₯Ό κ³ λ €ν•˜λŠ”κ²Œ 쉽지 μ•Šμ•˜λ‹€... dp 배열을 λœ»ν•˜λŠ” check의 μ •μ˜λŠ” λ‹€μŒκ³Ό κ°™λ‹€. μ‚Όμ°¨μ›λ°°μ—΄λ‘œ λͺ¨λ“ κ°’은 '-'둜 μ΄ˆκΈ°ν™”ν–ˆλ‹€.

 

check[k][i][j] = k+1 번째 μˆ«μžμ—μ„œ i 왼손, j 였λ₯Έμ†μΌ λ•Œμ˜ μ΅œμ†Œ κ°€μ€‘μΉ˜

 

μœ„μ—μ„œ ν’€μ—ˆλ˜ μ˜€λ‹΅μ˜ λ‚΄μš©μ„ 일뢀 μ‚¬μš©ν–ˆλ‹€.

board(λ³€μˆ˜) =  이차원배열둜 인덱슀λ₯Ό 번호둜 κ°€μ§€κ³  각 값은 λ°°μ—΄λ‘œ μ’Œν‘œλ₯Ό κ°€μ§„λ‹€.

calcDist(ν•¨μˆ˜) = 번호 μ‚¬μ΄μ˜ κ°€μ€‘μΉ˜λ₯Ό κ΅¬ν•˜λŠ” ν•¨μˆ˜. boardλ₯Ό κΈ°μ€€μœΌλ‘œ 두 수 μ‚¬μ΄μ˜ 거리λ₯Ό κ΅¬ν•˜κ³  거리λ₯Ό 계산해 κ°€μ€‘μΉ˜λ₯Ό ꡬ함.

 

숫자(numbers)λ₯Ό μˆœνšŒν•˜λ©΄μ„œ 왼손 0~9 였λ₯Έμ† 0~9인 경우λ₯Ό ν™•μΈν–ˆλ‹€.

 

첫번째둜 계산할 수 μžˆλŠ” μ΄ˆκΈ°κ°’μ΄ μžˆμ–΄μ•Ό ν•˜λ―€λ‘œ check[0]에 λŒ€ν•΄ μ΄ˆκΈ°ν™”λ₯Ό μ‹œμΌœμ€¬λ‹€.

 

  • check[0][4][numbers[0]] = calcDist(6, numbers[0]);
  • check[0][numbers[0]][6] = calcDist(4, numbers[0]);

 

같은 λ²ˆν˜Έμ— 양손이 올라갈 수 μ—†μœΌλ―€λ‘œ i와 jκ°€ 같은 κ²½μš°λŠ” continue μ‹œμΌœμ£Όκ³  λ§Œμ•½ check상 이전 값이 '-'인 경우 κ°€λŠ₯ν•œ κ²½μš°κ°€ μ—†κΈ° λ•Œλ¬Έμ— continue μ‹œμΌœμ€¬λ‹€.

 

check[k-1][i][j]κ°€ μžˆλŠ” 경우 왼손 λ˜λŠ” 였λ₯Έμ†μ„ 움직여 ν˜„μž¬ 번호λ₯Ό λˆ„λ₯΄λŠ” 상황을 κ³„μ‚°ν–ˆλ‹€. calcDist ν•¨μˆ˜λ₯Ό 각각 μ‚¬μš©ν–ˆλ‹€.

 

  • check[k][i][ν˜„μž¬μˆ«μž] -> 왼손이 ν˜„μž¬μˆ«μžλ‘œ 움직인 경우
  • check[k][ν˜„μž¬μˆ«μž][j] -> 였λ₯Έμ†μ΄ ν˜„μž¬μˆ«μžλ‘œ 움직인 경우

이λ₯Ό λ°˜λ³΅ν•˜κ³  checkλ°°μ—΄(dpλ°°μ—΄)μ—μ„œ λ§ˆμ§€λ§‰ μˆ«μžμ— ν•΄λ‹Ήν•˜λŠ” numbers.length-1번째λ₯Ό ν™•μΈν•œλ‹€. check[numbers.length-1] λ§ˆμ§€λ§‰ μˆ«μžκΉŒμ§€ λͺ¨λ‘ μˆœνšŒν–ˆμ„ λ•Œ 왼손 iμœ„μΉ˜, 였λ₯Έμ† j μœ„μΉ˜μΌ λ•Œμ˜ κ°€μ€‘μΉ˜μ— λŒ€ν•œ 값을 κ°€μ§€κ³  μžˆλ‹€. 이λ₯Ό μˆœνšŒν•˜λ©° κ°€μž₯ μž‘μ€ μ΅œμ†Œκ°’μ„ ꡬ해쀬닀.

 

⌨️ μ •λ‹΅ μ½”λ“œ 

function solution(numbers) {
  var answer = Infinity;

  const board = [
    [4, 2],
    [1, 1],
    [1, 2],
    [1, 3],
    [2, 1],
    [2, 2],
    [2, 3],
    [3, 1],
    [3, 2],
    [3, 3],
  ];

  const check = [...Array(numbers.length)].map(_ => [...Array(10)].map(_ => new Array(10).fill('-')));

  const calcDist = (now, target) => {
    if (now == target) return 1;

    const [nowX, nowY] = board[now];
    const [targetX, targetY] = board[target];

    let x = Math.abs(nowX - targetX);
    let y = Math.abs(nowY - targetY);

    let cnt = 0;
    while (x || y) {
      if (x && y) {
        cnt += 3;
        x -= 1;
        y -= 1;
      } else if (x) {
        cnt += 2 * x;
        x = 0;
      } else if (y) {
        cnt += 2 * y;
        y = 0;
      }
    }
    return cnt;
  };

  check[0][4][numbers[0]] = calcDist(6, numbers[0]);
  check[0][numbers[0]][6] = calcDist(4, numbers[0]);

  for (let k = 1; k < numbers.length; k++) {
    for (let i = 0; i < 10; i++) {
      for (let j = 0; j < 10; j++) {
        if (i === j) continue;
        if (check[k - 1][i][j] === '-') continue;

        const calc1 = check[k - 1][i][j] + calcDist(j, numbers[k]);
        if (check[k][i][numbers[k]] === '-') check[k][i][numbers[k]] = calc1;
        else check[k][i][numbers[k]] = Math.min(check[k][i][numbers[k]], calc1);

        const calc2 = check[k - 1][i][j] + calcDist(i, numbers[k]);
        if (check[k][numbers[k]][j] === '-') check[k][numbers[k]][j] = calc2;
        else check[k][numbers[k]][j] = Math.min(check[k][numbers[k]][j], calc2);
      }
    }
  }

  for (let i = 0; i < 10; i++) {
    for (let j = 0; j < 10; j++) {
      if (check[numbers.length - 1][i][j] === '-') continue;
      answer = Math.min(answer, check[numbers.length - 1][i][j]);
    }
  }

  return answer;
}