문제
우현이는 어린 시절, 지구 외의 다른 행성에서도 인류들이 살아갈 수 있는 미래가 오리라 믿었다. 그리고 그가 지구라는 세상에 발을 내려 놓은 지 23년이 지난 지금, 세계 최연소 ASNA 우주 비행사가 되어 새로운 세계에 발을 내려 놓는 영광의 순간을 기다리고 있다.
그가 탑승하게 될 우주선은 Alpha Centauri라는 새로운 인류의 보금자리를 개척하기 위한 대규모 생활 유지 시스템을 탑재하고 있기 때문에, 그 크기와 질량이 엄청난 이유로 최신기술력을 총 동원하여 개발한 공간이동 장치를 탑재하였다. 하지만 이 공간이동 장치는 이동 거리를 급격하게 늘릴 경우 기계에 심각한 결함이 발생하는 단점이 있어서, 이전 작동시기에 k광년을 이동하였을 때는 k-1 , k 혹은 k+1 광년만을 다시 이동할 수 있다. 예를 들어, 이 장치를 처음 작동시킬 경우 -1 , 0 , 1 광년을 이론상 이동할 수 있으나 사실상 음수 혹은 0 거리만큼의 이동은 의미가 없으므로 1 광년을 이동할 수 있으며, 그 다음에는 0 , 1 , 2 광년을 이동할 수 있는 것이다. ( 여기서 다시 2광년을 이동한다면 다음 시기엔 1, 2, 3 광년을 이동할 수 있다. )

김우현은 공간이동 장치 작동시의 에너지 소모가 크다는 점을 잘 알고 있기 때문에 x지점에서 y지점을 향해 최소한의 작동 횟수로 이동하려 한다. 하지만 y지점에 도착해서도 공간 이동장치의 안전성을 위하여 y지점에 도착하기 바로 직전의 이동거리는 반드시 1광년으로 하려 한다.
김우현을 위해 x지점부터 정확히 y지점으로 이동하는데 필요한 공간 이동 장치 작동 횟수의 최솟값을 구하는 프로그램을 작성하라.
입력
입력의 첫 줄에는 테스트케이스의 개수 T가 주어진다. 각각의 테스트 케이스에 대해 현재 위치 x 와 목표 위치 y 가 정수로 주어지며, x는 항상 y보다 작은 값을 갖는다. (0 ≤ x < y < 231)
출력
각 테스트 케이스에 대해 x지점으로부터 y지점까지 정확히 도달하는데 필요한 최소한의 공간이동 장치 작동 횟수를 출력한다.
| 예제입력 | 예제출력 |
| 3 0 3 1 5 45 50 |
3 3 4 |
❌ 풀이
const input=require('fs').readFileSync('/dev/stdin').toString().split('\n');
const t=Number(input.shift());
for(let i=0;i<t;i++){
let distance=0;
let count=0;
let [x,y]=input[i].split(' ').map(v=>Number(v));
distance= y-x;
for(let j=1;;j++){
let aaa=distance-(j*2);
if(aaa>=j-1&&aaa<=j+1){
count+=3;
break;
}else if(aaa>j+1&&aaa<j*2){
count+=4;
break;
}else{
count+=2;
distance=aaa;
}
}console.log(count);
}
우선 도착지점과 출발지점의 거리를 구해준뒤
출발지점에서 1 도착지점에서 1씩 뺀다는 생각으로
j*2씩 빼며 count +=2 를
거리를 임의변수 aaa로 값을 바꿔주며 반복했다.
그러다 남은 거리가 해당조건에 만족할경우
값에 맞게 count를 올려준뒤 출력하게했다.
예제를 그대로 넣고했을때 예제출력과같이 3 3 4가 출력됐는데
역시 숫자를 높여보니 허술한데가 많았다.
숫자가 높아질수록 중간에 거리의 차이가 커지는경우가 있는데
이런경우에까지 맞는조건을 다 찾아넣어주기는 불가능으로보였다.
그래서 이풀이는 잘못된방향이라고 생각해 결국 구글링.
✅ 다른분의 풀이
| distance | move | count | max |
| 1 | 1 | 1 | 1 |
| 2 | 1 1 | 2 | 1 |
| 3 | 1 1 1 | 3 | 1 |
| 4 | 1 2 1 | 3 | 2 |
| 5 | 1 2 1 1 | 4 | 2 |
| 6 | 1 2 2 1 | 4 | 2 |
| 7 | 1 2 2 1 1 | 5 | 2 |
| 8 | 1 2 2 2 1 | 5 | 2 |
| 9 | 1 2 3 2 1 | 5 | 3 |
| 10 | 1 2 3 2 1 1 | 6 | 3 |
이 형태에서
우리가 필요한건 count 즉 이동횟수이므로 횟수를 기준으로 다시 표를 만들어보면
| count | distance | move | max |
| 1 | 1 | 1 | 1 |
| 2 | 2 | 1 1 | 1 |
| 3 | 4 | 1 2 1 | 2 |
| 4 | 6 | 1 2 2 1 | 2 |
| 5 | 9 | 1 2 3 2 1 | 3 |
| 6 | 12 | 1 2 3 3 2 1 | 3 |
| 7 | 16 | 1 2 3 4 3 2 1 | 4 |
| 8 | 20 | 1 2 3 4 4 3 2 1 | 4 |
| 9 | 25 | 1 2 3 4 5 4 3 2 1 | 5 |
| 10 | 30 | 1 2 3 4 5 5 4 3 2 1 | 5 |
count에따라 한번에 이동가능한 최대거리 max를 참고해 규칙을 찾아보면
1. max가 1씩증가하면서 2번씩 반복된다.
2. Distance는 이전거리와 최댓값과의 차이가 max가 증가하는 규칙과 동일
3. max가 변하는 지점의 distance는 max의 제곱값.
| distance | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 |
| count | 1 | 2 | 3 | 3 | 4 | 4 | 5 | 5 | 5 | 6 | 6 | 6 | 7 | 7 | 7 | 7 | 8 | 8 |
| max | 1 | 1 | 1 | 2 | 2 | 2 | 2 | 2 | 3 | 3 | 3 | 3 | 3 | 3 | 3 | 4 | 4 | 4 |
| 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 |
| 8 | 8 | 9 | 9 | 9 | 9 | 9 | 10 | 10 | 10 | 10 | 10 | 11 | 11 | 11 | 11 | 11 | 11 | 12 |
| 4 | 4 | 4 | 4 | 4 | 4 | 5 | 5 | 5 | 5 | 5 | 5 | 5 | 5 | 5 | 5 | 5 | 6 | 7 |
count가 변하는 지점
max가 변하는지점
❗ 규칙1. max의 값은 distance의 루트값에서 소수점을 버린 정수값과 같다,
max=Math.floor(Math.sqrt(distance));
✅ Math.sqrt 주어진 숫자에 루트를 씌우며 숫자가 음수일경우 NaN을 반환
Math.sqrt(9); // 3
Math.sqrt(2); // 1.414213562373095
Math.sqrt(1); // 1
Math.sqrt(0); // 0
Math.sqrt(-1); // NaN
❗ 규칙2. max 가 변하는 지점과 다음지점사이에는 count가 두번씩 변한다.
즉 녹색구간 사이에 노란색이 두번씩 포함된다.
❗ 규칙3. 녹색 칸 다음에는 반드시 count가 변한다.
❗ 규칙4. max값이 변할때의 count는 다음 수식을 따른다.
count= 2 * max -1
규칙을 찾았으니 조건문을 하나씩 만들어나가면 된다.
✅ 첫번째 조건문
max는 distance의 제곱근중 정수부분만 취한값.
만약 정수부분만 취한값과 소수점까지 취한값이 같다면?
예를들어 9의 제곱근은 3이고 소수점을 버린 max도 3이다.
이렇게 딱떨어진다면 규칙4에 의해
count=2*max-1이 된다.
let distance = y-x;
let max = Math.floor(Math.sqrt(distance));
if(max==Math.sqrt(distance)){
count=2*max-1;
}
✅ 두번째 조건문
max의 제곱근이 정수로 떨어지는경우는 첫번째 조건문에서 제외시켰으니
다음구간부터 다음max이전구간을 구해야한다.
| 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
| 5 | 6 | 6 | 6 | 7 | 7 | 7 | 7 |
| 3 | 3 | 3 | 3 | 3 | 3 | 3 | 4 |
여기서 제곱근이 딱떨어지는 조건에 부합했던 거리 9와 16은 제외되었다.
| 10 | 11 | 12 | 13 | 14 | 15 |
| 6 | 6 | 6 | 7 | 7 | 7 |
| 3 | 3 | 3 | 3 | 3 | 3 |
여기서 규칙을 찾아야하는데
max값이 3인데 count가 변하는 지점을 나누어 묶어보면
묶인갯수는 max값과 같다는 것이 보인다.
다른구간도 마찬가지로 max값이 5인 구간은 5개씩 묶인다.
❗ 그럼 첫번째로 묶인 10 11 12 구간은 다음과같다.
max*max < distance ≤ max*max+max
// 9 < distance ≤ 12
그리고 해당 구간의 count값은 2*max가 된다.
이것들을 토대로 조건문을 만들어보면
let distance = y-x;
let max = Math.floor(Math.sqrt(distance));
if(max==Math.sqrt(distance)){
count=2*max-1;
}
else if(distance <= max*max+max){
count=2*max;
}
앞선 첫번째 조건문에서 이미 max*max부분은 걸러졌으니 최소조건은 필요가없다.
✅ 세번째 조건문
❗ 두번째로 묶인 13 14 15 구간은 else로 해결하면 되고
이때 count의 값만 구하면된다.
첫번째구간의 count값은 6 6 6
두번째구간의 count값은 7 7 7 이었으므로
count = 2*max+1
let distance = y-x;
let max = Math.floor(Math.sqrt(distance));
if(max==Math.sqrt(distance)){
count=2*max-1;
}
else if(distance <= max*max+max){
count=2*max;
}
else{
count=2*max+1;
}
⭕ 참고해서 다시풀이
const input=require('fs').readFileSync('/dev/stdin').toString().split('\n');
const t=Number(input.shift());
for(let i=0;i<t;i++){
let distance=0;
let [x,y]=input[i].split(' ').map(v=>Number(v));
distance= y-x;
let max=Math.floor(Math.sqrt(distance));
if(max==Math.sqrt(distance)){
console.log(max*2-1);
}else if(distance <= max*max+max){
console.log(max*2);
}else {
console.log(max*2+1);
}
};
지금까지는 구글링을 하면 제출하신 답안코드를 보고 이해하는 방식이었으나
이번엔 해당 문제를 자바스크립트로 풀이한분들이 많이 계시지않아
다른언어라도 풀이과정을 참고하면 해결할 수 있을것같아 둘러보던와중에
처음부터 꼼꼼하게 이해시켜주며 설명해주시는 글을 보고
어떻게 저런규칙을 찾아서 저렇게 구현할수있지..싶어 감탄했다.
-출처
https://www.acmicpc.net/problem/1011
'Algorithm > Baekjoon(Node.js)' 카테고리의 다른 글
| [JavaSrcipt] Baekjoon - 2581 : 소수 (0) | 2021.09.29 |
|---|---|
| [JavaSrcipt] Baekjoon - 1978 : 소수 찾기 (함수중단 return) (0) | 2021.09.28 |
| [JavaSrcipt] Baekjoon - 10757 : 큰 수 A+B (BigInt) (0) | 2021.09.26 |
| [JavaSrcipt] Baekjoon - 2839 : 설탕 배달 (0) | 2021.09.25 |
| [JavaSrcipt] Baekjoon - 2775 : 부녀회장이 될테야 (shift) (0) | 2021.09.24 |