난이도 level1 문제
programmers.co.kr/learn/courses/30/lessons/67256
이 문제에서 나는 서로다른 위치의 키패드의 이동 거리를 계산 할 수 있다면 문제를 쉽게 풀 수 있을 것 같아서
패턴을 찾기 시작했고 아래와 같은 패턴을 찾게됐다.
숫자 5에서 상하좌우 로 이동시
상 : -3, 하 : +3
좌 : -1, 우 : 1
모든 위치에서 상하좌우로 움직일 때 이동 할 곳이 있다면 위의 패턴이 동일하게 적용 되는 걸 확인 할 수 있다.
그렇다면 상하는 +-3 좌우는 +-1 이기 때문에 절대값로 보면 상하 : 3 , 좌우: 1 로 볼 수 있다
이제 실제로 거리를 구하는데 적용해 보자!!
우선 이동할 위치를 X라고 하고 도착지를 Y로한다.
1. X-Y 의 절대값을 씌운다.
2. 절대값에서 상or하 로 이동할 수 있는 거리만큼 이동한다
(참고로 상하는 3씩 이동한다.)
3. 더이상 상하로 이동할 수 없다면 좌우로 이동한다
(좌우는 1씩 이동한다.)
상하이동거리 = | X - Y | / 3
좌우이동거리 = | X - Y | % 3
총 이동거리 = ( | X - Y | / 3 ) + ( | X - Y | % 3 )
실제로 값을 대입해 보자
X=1, Y=8
상하이동거리 = | 1 - 8 | / 3 = 2
좌우이동거리 = | 1 - 8 | % 3 = 1
총거리 = 상하이동거리 + 좌우이동거리 = 3
이제 패턴을 찾았기 때문에 우리는 문제를 매우 쉽게 풀 수 있다.
public class PressKeypad {
public static void main(String[] args) throws Exception {
int [] numbers1 = {1,3,4,5,8,2,1,4,5,9,5};
int [] numbers2 = {7,0,8,2,8,3,1,5,7,6,2};
int [] numbers3 = {1,2,3,4,5,6,7,8,9,0};
PressKeypad app = new PressKeypad();
System.out.println(app.solution(numbers1,"right"));
}
/* T = 타겟, L = 왼손엄지, R = 오른손엄지
*
* 1. T가 2,5,8,9 에 속하지 않으면 L과 R의 기본 선택번호화 순서대로 비교해서 터치
* 2. T가 2,5,8,9 에 속하면 왼손거리 = ( (|L-T|)/3 ) + ( (|L-T|)%3 )
* 오른손거리 = ( (|R-T|)/3 ) + ( (|R-T|)%3 )
* 를 각각 구한다.
* 3. 만약 T가 0이라면 11로 변환해서 값을 구한다.
* 4. 구한 거리의 크기가 같으면 우선순위 손으로 터치하고 터치 한 손의 위치를 변경한다.
* 5. 구한 거리의 크기가 다르면 가까운 손으로 터치하고 터치 한 손의 위치를 변경한다.
*/
static int leftLocation = 10; // 왼손의 위치
static int rightLocation = 12; // 오른손의 위치
public String solution(int[] numbers, String hand) {
int [] leftArea = {1,4,7}; // 왼쪽 영역숫자
int [] targetList = {2,5,8,0}; // 경쟁영역 숫자
String defaultHand = "left".equals(hand) ? "L" : "R"; // 기본으로 들어오는 손 변경저장
StringBuffer result = new StringBuffer(); // 결과 버퍼
int leftOrRight = 0; // 왼손거리 - 오른손거리
int targetTemp = 0; // 터치 해야되는 숫자
for(int target : numbers) {
if(isEasyChoice(target,targetList)) { // 경쟁영역의 숫자가 아닌 경우
pressEasyChoice(target,leftArea,result); // 간단 버튼클릭 수행
} else { // 경쟁영역의 숫자인 경우
targetTemp = (target == 0) ? 11 : target;
leftOrRight = getDistance(leftLocation,targetTemp) - getDistance(rightLocation,targetTemp);
pressHardChoice(leftOrRight,targetTemp,defaultHand,result); // 경쟁영역의 숫자 버튼클릭 수행
}
}
return result.toString();
}
// 간단 버튼클릭 수행
public void pressEasyChoice(int target, int [] leftArea, StringBuffer result) {
if(isLeftTouch(target,leftArea)) { // 왼손으로 터치
result.append("L");
leftLocation = target;
} else { // 오른손으로 터치
result.append("R");
rightLocation = target;
}
}
// 거리계산 후 버튼클릭
public void pressHardChoice(int leftOrRight, int targetTemp,String defaultHand,StringBuffer result) {
if(leftOrRight == 0) {
result.append(defaultHand);
if("L".equals(defaultHand)) {
leftLocation = targetTemp;
} else {
rightLocation = targetTemp;
}
} else if(leftOrRight < 0) {
result.append("L");
leftLocation = targetTemp;
} else {
result.append("R");
rightLocation = targetTemp;
}
}
// 왼손에 해당 되는지 영역인지 체크한다.
public boolean isLeftTouch(int compareNum , int [] leftArea) {
for(int target : leftArea) {
if(target == compareNum) return true;
}
return false;
}
// 거리 검사가 필요한지 체크한다.
public boolean isEasyChoice(int compareNum, int[] targetList) {
for(int target : targetList) {
if(target == compareNum) return false;
}
return true;
}
// 두 숫자의 이동 거리를 계산한다.
// 두 point의 거리 = ( (|L-T|)/3 ) + ( (|L-T|)%3 )
public int getDistance(int point, int target) {
int subStracAbsNum = Math.abs(point - target);
return (subStracAbsNum/3) + (subStracAbsNum%3);
}
}