[프로그래머스 Level.3] 자물쇠와 열쇠 (2020 KAKAO BLIND RECRUITMENT) (Java)
문제 링크
https://school.programmers.co.kr/learn/courses/30/lessons/60059
프로그래머스
코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.
programmers.co.kr
코딩테스트 연습 > 2020 KAKAO BLIND RECRUITMENT > 자물쇠와 열쇠
문제 설명
고고학자인 "튜브"는 고대 유적지에서 보물과 유적이 가득할 것으로 추정되는 비밀의 문을 발견하였습니다. 그런데 문을 열려고 살펴보니 특이한 형태의 자물쇠로 잠겨 있었고 문 앞에는 특이한 형태의 열쇠와 함께 자물쇠를 푸는 방법에 대해 다음과 같이 설명해 주는 종이가 발견되었습니다.
잠겨있는 자물쇠는 격자 한 칸의 크기가 1 x 1인 N x N 크기의 정사각 격자 형태이고 특이한 모양의 열쇠는 M x M 크기인 정사각 격자 형태로 되어 있습니다.
자물쇠에는 홈이 파여 있고 열쇠 또한 홈과 돌기 부분이 있습니다. 열쇠는 회전과 이동이 가능하며 열쇠의 돌기 부분을 자물쇠의 홈 부분에 딱 맞게 채우면 자물쇠가 열리게 되는 구조입니다. 자물쇠 영역을 벗어난 부분에 있는 열쇠의 홈과 돌기는 자물쇠를 여는 데 영향을 주지 않지만, 자물쇠 영역 내에서는 열쇠의 돌기 부분과 자물쇠의 홈 부분이 정확히 일치해야 하며 열쇠의 돌기와 자물쇠의 돌기가 만나서는 안됩니다. 또한 자물쇠의 모든 홈을 채워 비어있는 곳이 없어야 자물쇠를 열 수 있습니다.
열쇠를 나타내는 2차원 배열 key와 자물쇠를 나타내는 2차원 배열 lock이 매개변수로 주어질 때, 열쇠로 자물쇠를 열수 있으면 true를, 열 수 없으면 false를 return 하도록 solution 함수를 완성해주세요.
제한사항
- key는 M x M(3 ≤ M ≤ 20, M은 자연수)크기 2차원 배열입니다.
- lock은 N x N(3 ≤ N ≤ 20, N은 자연수)크기 2차원 배열입니다.
- M은 항상 N 이하입니다.
- key와 lock의 원소는 0 또는 1로 이루어져 있습니다.
- 0은 홈 부분, 1은 돌기 부분을 나타냅니다.
입출력 예
key | lock | result |
[[0, 0, 0], [1, 0, 0], [0, 1, 1]] | [[1, 1, 1], [1, 1, 0], [1, 0, 1]] | true |
입출력 예에 대한 설명

key를 시계 방향으로 90도 회전하고, 오른쪽으로 한 칸, 아래로 한 칸 이동하면 lock의 홈 부분을 정확히 모두 채울 수 있습니다.
나의 코드
class Solution {
int[][] newLock;
public boolean solution(int[][] key, int[][] lock) {
boolean answer = false;
int n = lock.length;
int m = key.length;
int newM = n + m * 2 - 2; // 자물쇠 크기 + 열쇠 크기 * 2 - 2 (자물쇠의 양쪽에 열쇠 크기만큼 추가하고 열쇠가 한 부분에는 걸치도록 -2를 해준다)
newLock = new int[newM][newM];
for(int i=m-1; i<n+m-1; i++) {
for(int j=m-1; j<n+m-1; j++) {
newLock[i][j] = lock[i-(m-1)][j-(m-1)];
}
}
for(int i=0; i<4; i++) {
if(check(newM, n, m, key)) return true;
rotationKey(key); // 시계방향 90도 회전
}
return answer;
}
private boolean check(int newM, int n, int m, int[][] key) {
int[][] lock = newLock;
for(int i=0; i<newM-m+1; i++) {
for(int j=0; j<newM-m+1; j++) {
// 자물쇠 키 결합
for(int k=0; k<m; k++) {
for(int l=0; l<m; l++) {
lock[k+i][l+j] += key[k][l];
}
}
// 자물쇠 키 맞는지 체크
boolean flag = true;
for(int k=m-1; k<m+n-1; k++) {
for(int l=m-1; l<m+n-1; l++) {
if(lock[k][l] != 1) {
flag = false;
break;
}
}
if(!flag) break;
}
if(flag) return true;
// lock 되돌리기
for(int k=0; k<m; k++) {
for(int l=0; l<m; l++) {
lock[k+i][l+j] -= key[k][l];
}
}
}
}
return false;
}
private void rotationKey(int[][] key) {
int[][] tempKey = new int[key.length][key.length];
// 시계 방향 90도 회전
for(int i=0; i<key.length; i++) {
for(int j=0; j<key.length; j++) {
tempKey[i][j] = key[key.length-1-j][i];
}
}
// key = tempKey; // 안된다 (얕은 복사로 주소를 이어주는 방법이므로 key가 tempKey의 주소를 보는 것임)
for(int i=0; i<key.length; i++) {
key[i] = tempKey[i].clone(); // clone() 메서드는 데이터 자체를 복사하여 새로 생성
// for(int j=0; j<key.length; j++) {
// key[i][j] = tempKey[i][j];
// }
}
}
}
풀이
- 열쇠가 자물쇠에 맞는지 확인하는 것으로 자물쇠의 영역 밖의 부분은 상관이 없다. 따라서 열쇠가 자물쇠의 해당 부분에 맞는지 확인하기 위해 열쇠를 일부분만 속하는 부분부터 확인해 나가기 위해 새로운 자물쇠를 만들어 크기를 기존 자물쇠 크기에 양 쪽에 열쇠 크기를 더한 것에서 한 부분씩은 자물쇠에 닿아야하기 때문에 -2를 해주도록 한다.(newM = n + m * 2 - 2)
- 그리고 새로운 자물쇠의 가운데에 기존 자물쇠의 데이터를 담도록 한다. 그러면 만약 자물쇠가 [[1, 1, 1], [1, 1, 0], [1, 0, 1]] 이고 열쇠가 [[0, 0, 0], [1, 0, 0], [0, 1, 1]] 라면 변환하면 다음과 같다.
0 0 0 0 0 0 0
0 0 0 0 0 0 0
0 0 1 1 1 0 0
0 0 1 1 0 0 0
0 0 1 0 1 0 0
0 0 0 0 0 0 0
0 0 0 0 0 0 0 - 이제 이 새로운 자물쇠에 열쇠를 결합했을 때 가운데 부분의 기존 자물쇠 부분이 전부 1이 되는지 확인하도록 한다.
- 자물쇠에 열쇠가 맨 왼쪽 위에있을 때부터 맨 오른쪽 아래로 갈 때까지 2중 for문을 돌면서 자물쇠에 열쇠를 결합한다.(자물쇠의 값에 열쇠 부분의 값을 더해주도록 한다)
- 결합 한 후에는 자물쇠의 키가 맞는지 확인하도록 한다.(새로운 자물쇠의 가운데 부분에 있는 기존 자물쇠 부분이 전부 1로 되었는지 확인한다)
- 만약 자물쇠의 키가 맞다면 true를 반환하면 되고 자물쇠의 키가 아니라면 자물쇠에 열쇠 부분을 더해주었으니 다시 열쇠 부분을 뺴주도록 한다.
- 자물쇠 부분을 열쇠로 전부 확인했는데도 맞는 키를 찾지못했으면 열쇠를 시계 방향으로 90도 회전하여 다시 확인하도록 한다.
- key의 크기만큼 임시 배열 tempKey를 만들고 key를 90도 회전하여 저장한 후에 이 tempKey를 다시 for문을 돌면서 key에 다시 tempKey에 있는 값들을 저장해주도록 한다. (key = tempKey는 얕은 복사로 key에 tempKey의 주소를 이어주는 방법이므로 key가 tempKey의 주소를 보게 되는 것이지 값을 복사해서 저장하는 것이 아니기 때문에 tempKey가 변경되거나 제거되면 key가 바뀌므로 사용하지 않도록 한다)
- 이렇게 90도 회전한 후에 다시 자물쇠에 결합하여 확인하고 맞지 않는다면 다시 시계 방향으로 90도 회전하여 결합하고 확인하는 방식으로 시계방향으로 모두 돌려가며 확인하도록 한다.
- 시계방향으로 회전해가며 전부 확인해도 맞는 키를 찾지 못했다면 열쇠가 맞지 않는 것이므로 false를 반환하도록 한다.