리코딩 : 알고리즘

11. 프로그래머스 - [카카오]비밀지도

BreezeBm 2021. 10. 1. 10:31

1. 문제

네오는 평소 프로도가 비상금을 숨겨놓는 장소를 알려줄 비밀지도를 손에 넣었다. 그런데 이 비밀지도는 숫자로 암호화되어 있어 위치를 확인하기 위해서는 암호를 해독해야 한다. 다행히 지도 암호를 해독할 방법을 적어놓은 메모도 함께 발견했다.

  1. 지도는 한 변의 길이가 n인 정사각형 배열 형태로, 각 칸은 "공백"(" ") 또는 "벽"("#") 두 종류로 이루어져 있다.
  2. 전체 지도는 두 장의 지도를 겹쳐서 얻을 수 있다. 각각 "지도 1"과 "지도 2"라고 하자. 지도 1 또는 지도 2 중 어느 하나라도 벽인 부분은 전체 지도에서도 벽이다. 지도 1과 지도 2에서 모두 공백인 부분은 전체 지도에서도 공백이다.
  3. "지도 1"과 "지도 2"는 각각 정수 배열로 암호화되어 있다.
  4. 암호화된 배열은 지도의 각 가로줄에서 벽 부분을 1, 공백 부분을 0으로 부호화했을 때 얻어지는 이진수에 해당하는 값의 배열이다.

 네오가 프로도의 비상금을 손에 넣을 수 있도록, 비밀지도의 암호를 해독하는 작업을 도와줄 프로그램을 작성하라

출처 : 프로그래머스

 

입출력 예제 1

매개변수
n 5
arr1 [9, 20, 28, 18, 11]
arr2 [30, 1, 21, 17, 28]
출력 ["#####","# # #", "### #", "# ##", "#####"]

입출력 예제 2

매개변수
n 6
arr1 [46, 33, 33 ,22, 31, 50]
arr2 [27 ,56, 19, 14, 14, 10]
출력 ["######", "### #", "## ##", " #### ", " #####", "### # "]

2. 나의 풀이

function solution(n, arr1, arr2) {
    let answer = [];
    let newArr1 = arr1.map(el => el.toString(2).padStart(n, 0));
    let newArr2 = arr2.map(el => el.toString(2).padStart(n, 0));
    for(let i = 0; i < n; i ++) {
        let word = "";
        for(let j = 0; j < n; j ++) {
            if(newArr1[i][j] === "1" || newArr2[i][j] === "1") {
                word += "#";
            } else {
                word += " ";
            }
        }
        answer.push(word);
    }
    return answer;
}

- 주어지는 배열의 값을 2진수로 변환을 한다.

- 이때, 만약 1이라는 값이 있으면 2진수로는 1이된다. 이렇게 되면 나중에 두개의 배열을 비교할 때 문제가 발생한다.
(입출력 예제 1번을 보면 1과 20을 비교해야 하는데, 1은 2진수로 1, 20은 2진수로 10100이다. 각각의 자리에서 비교가 불가능하다.)

- 그래서 padStart로 비교 길이 만큼 0으로 채워야 한다. (1인 경우에 00001로 만들어 준다.)

 

- 이중 반복문을 통해서, 각각의 2진수 배열을 확인해서 1이 있으면 #을, 없으면 " "를 붙여준다.

- 만들어진 값을 배열에 넣어 반환한다.

 

 처음에 계속해서 결과값이 [ "     ", "     ", "     ", "     ", "     " ]로만 찍혀서 왜 안되는 걸까 생각을 했을 때, 우선 1이라는 숫자가 2진수로 변환을 했을 때, 00001이 아닌, 1로 나온다는 것과, 반복문에서 newArr1[i][j] === 1이라고 작성을해서 오류가 났었다.

 

다른분의 풀이를 보니, 이전에 사용했던 메소드들로 체이닝을 해서 풀이한 것을 볼 수 있었다.

function solution(n, arr1, arr2) {
  let result = arr1.map((a, i) =>
    (a | arr2[i])	// 1
      .toString(2)	// 2
      .padStart(n, 0)	// 3
      .replace(/0/g, " ")	// 4
      .replace(/1/g, "#")	// 5
  );
  return result;

1. ???????????

2. 2진수로 변환

3. 0으로 자릿수를 채우기

4. 0을 찾아서  " "으로 치환

5. 1을 찾아서 "#"으로 치환

 

도대체.. 1번은... 무엇인가,,??????? 

1번은 찾아보니 비트 연산이었다. || 이거나, &&는 많이 봐왔는데 하나만 있는건 처음 보았다. 우선 비트는(bit)는 Binary Digit의 줄임 말이다. 컴퓨터가 처리하는 데이터의 최소단위 이다. 컴퓨터는 2진수를 사용한다. 즉 0과 1로써 구성이 된다. 그래서 비트연산자를 안다면 그렇게도 활용할 수 있도록 2진수를 준게 아닐까 싶다. 

 

비트연산자에는 

 OR 연산자 : |

 AND 연산자 : &

 XOR 연산자 : ^

 NOT 연산자 : ~

 left shift 연산자 : <<

 right shift 연산자 : >> 가 있다.

비트연산자 설명
& 대응되는 비트가 모두 1이면 1을 반환함 (비트 AND 연산)
| 대응되는 비트 중에서 하나라도 1이면 1을 반환함 (비트 OR 연산)
^ 대응되는 비트가 서로 다르면 1을 반환함 (비트 XOR 연산)
~ 비트를 1이면 0으로 0이면 1로 반전시킴 (비트 NOT 연산)
<< 지정한 수만큼 비트들을 전부 왼쪽으로 이동시킴 (left shift 연산)
>> 부호를 유지하면서 지정된 수만큼 비트를 전부 오른쪽으로 이동시킴 (right shift 연산)

 

즉, 1번에서 두개의 배열을 이진수 값을 비교할 때, 비트 연산 OR을 사용해서 비교를 한다.

9와 30을 비교하게 되면 9는 01001, 30은 11110인데, 2진수의 각자리를 비트연산으로 비교해보면

index 0 1 2 3 4
9 0 1 0 0 1
30 1 1 1 1 0
OR 1 1 1 1 1

각 자리를 대응했을 때 다 1이 있기 떄문에 OR연산은 1을 반환하게 된다.

만약 20과 1을 비교하게 되면 20은 10100, 1은 00001인데, 2진수의 각자리를 비트연산으로 비교해보면

index 0 1 2 3 4
20 1 0 1 0 0
1 0 0 0 0 1
OR 1 0 1 0 1

index1번과 3번은 둘다 1이 없기 때문에 0을 반환하게 된다. 

 

이후에 메소드 체이닝을 통해 1인경우 #을, 0인 경우 " "을 반환하게 되어서 결과값을 도출 할 수 있게 된다.!

공부공부...

 

비트연산 출처: https://tcpschool.com/c/c_operator_bitwise