본문 바로가기
개발자로 나아가는 나의 언어들/JAVA

[JAVA/Softeer] 위험한 효도 - 코딩테스트 생각의 차이 비교

by 승싱생숭 2024. 10. 17.
728x90

 

 

 위험한 효도

문제

 

무궁화 꽃이 피었습니다와 같은 방식으로 술래에게 갔다가 오는 거리를 계산하는 문제이다.

 

문제 해석 방향

 

이번 문제의 풀이는 2가지 방향으로 풀어보았다.

 

1. 문제 그대로를 조건문, 반복문을 통해 구현하는 방법

2. 수학적 풀이를 활용하여 삼항연산자를 통해 구현하는 방법

 

 

조건, 반복문을 통한 구현

 

처음 문제를 보고 나서는 술래가 앞을 보고, 뒤를 도는 과정을 반복문을 통해서 구현하면 되겠다라고 생각이 들었다.

그래서 조건을 그리기 위해 전체적인 흐름을 그려보았다.

 

 

처음 갈때 는 a부터 시작해서 abab반복해서 다음값이 주어질 것이고,

돌아올때는 b부터 시작해서 baba 반복될 것이라고 생각하고 그려보았다.

 

그런데 이제 갈때나 올때나 d에 주어진 길이를 넘으면 안되기 때문에 sum이라는 변수에 총길이를 넘었는지 체크하도록 로직을 걸어야 겠다고 생각하였다.

 

sum 값은 갈때, 올때 d의 길이를 넘었는지 체크하는 용도이기 때문에 올때가 되었을때는 0으로 초기화 후 다시 값을 더하도록 해야겠다 생각하였고, result는 최종 값을 구하기 위해 계속 데이터를 축적해나가는 방식으로 생각하였다.

 

그리고 나서 갈 때와 올때의 구분을 주어야하는데 check라는 변수를 통하여 갈때인지 올때인지 구분하여서 ab로 시작할지 ba로 시작할지 기준을 잡도록 하였다.

 

이 check라는 값을 변경하는 기준은 check가 0일 때, sum값이 10을 넘기면 check값을 1로 변경해주도록 할 것이다.

 

그리고 abab, baba를 반복하여 result에 값을 더해주지만 맨 마지막에는 그 값을 그대로 넣는 것이아니라,

총 길이까지 남은 거리를 더해 주어야한다.

 

이러한 생각들을 가지고 만든 반복, 조건문만을 통한 코드를 한번 보자.

	public static void main(String[] args)  throws Exception {
		
		System.setIn(new FileInputStream("src/test/input.txt"));
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		String[] InputArr = (br.readLine()).split(" "); 
		int d = Integer.parseInt(InputArr[2]); // 총길이
		int a = Integer.parseInt(InputArr[0]); // a
		int b = Integer.parseInt(InputArr[1]); // b
		int sum = 0; // 현재까지의 이동거리를 담을 변수
		int result = 0; // 최종 값
		int chk = 0; // 술래에게 갈때와 올때 구분
		
		
		while (true) { //최종결과값이 나올때까지 반복
			
			if(chk == 1 && sum >= d) { //최종 결과가 나오면 stop
				break;
			}
			
			if(chk == 0) { //갈때
				
				if(sum >= d) { // 술래에게 도착했으면 chk값 변경, sum 초기화 후 올때로 이동
					sum = 0;
					chk = 1;
					continue;
				}
				
				if(sum + a > d) { // 현재까지의 이동거리에 a를 더했는데 술래에게 도착할 거리를 넘은 경우
					result += d - sum; 
					sum += d - sum; // 현재까지의 이동거리에 ( 총거리 - 이동한거리 = 남은거리 )를 더해준다. ( 첫번째 예시 10 - 7 = 3 ) 
				}else{ // 현재까지의 이동거리에 a를 더해도 술래에게 도착할 거리를 넘지 않은 경우
					sum += a; // 현재까지의 이동거리에 a를 더해준다.
					if(sum == d) { // 단, 총 이동거리(=d)에 도달한 경우 술래가 앞을 보고있는 시간은 더해주지않는다.
						result += a;
					}else { // 총이동거리(=d)에 도달하지 않은 경우 술래가 앞을 보고있는 시간도 더해준다.
						result += a + b;
					}
				}
						

			}else{ // 올때
				
				if(sum + b > d) { // 현재까지의 이동거리에 b를 더했는데 술래에게 도착할 거리를 넘은 경우
					result += d-sum;
					sum += d-sum ; // 현재까지의 이동거리에 ( 총거리 - 이동한거리 = 남은거리 )를 더해준다. ( 첫번째 예시 10 - 9 = 1 ) 
				}else{ // 현재까지의 이동거리에 b를 더해도 술래에게 도착할 거리를 넘지 않은 경우
					sum += b; // 현재까지의 이동거리에 b를 더해준다.
					if(sum == d) { // 단, 총 이동거리(=d)에 도달한 경우 술래가 앞을 보고있는 시간은 더해주지않는다.
						result += b;
					}else { // 총 이동거리(=d)에 도달하지 않은 경우 술래가 앞을 보고있는 시간도 더해준다.
						result += b + a;
					}
					
				}
			}
		}
		
		System.out.println(result);
		
	}

 

 

옆에 주석을 자세히 달아놓으려고 노력했지만, 상당히 지저분하다고 느껴졌다 ㅎㅎ...

 

진짜 이렇게 푸는게 맞을까? 생각하며 다시 풀어본게 바로 수학적 계산으로 접근해보는 것이었다.

 

수학적 풀이를 활용하여 구현

 

아래 이미지의 값에 써있는 것들 중 음영처리 된 부분이 술래가 뒤를 돌아본 시간이다.

(3)과 같이 괄호 쳐져있는 값이 실제로 마지막에 이동한 거리입니다.

 


음영친 부분을 제외하고 갈때 올때 값을 더해보면 갈때 10, 올때 10 총 2d의 값을 가져가게 됩니다.

 

그러면 술래가 뒤를 돌아본 시간 즉, 음영 쳐져있는 횟수만 구하면 됩니다.

 

술래에게 갈때는 b가 뒤를 돌아본 시간, 돌아올때는 a가 뒤를 돌아본 시간이고

이 횟수를 구하려면 갈 때는 총 길이(d)를 a로 나누어주면 술래가 몇번 뒤를 돌아보았는지 나오게 되고

올 때에는 총 길이(d)를 b로 나누어주면 술래가 몇번 뒤돌아 보았는지 나오게된다.

 

처음에는 왜 나눠야하는지 이해가 안갔는데,

총길이는 고정되어있고 이동한거리는 a나 b이기 때문에 갈때의 기준으로 a로 나눴을때 몫이 1이 나오니

a로 한번 이동하고 술래가 한번 뒤돌아보고 남은 거리를 이동한다는 전제가 된다.

 

숫자를 나열해놓고 보니 이 계산에 이해가 쉬웠다.

 

그래서 2d + d/a * b + d/b * a = result 라는 수식이 나오게된다.

 

이렇게 생각하면 쉬운데 하나 더 생각해야할게 있다.

 

d와 a, d와 b의 값이 같은경우이다.

 

그런 경우에는 몫이 1이 나오지만 나머지가 0이기때문에 술래가 뒤돌아볼 시간이 없다.

 

이 경우의 수에는 몫을 0으로 처리하면 된다.

 

728x90

 

최종코드

 

최종 코드를 보면 아래와 같다.

	public static void main(String[] args)  throws Exception {
		
		System.setIn(new FileInputStream("src/test/input.txt"));
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		String[] InputArr = (br.readLine()).split(" "); 
		int d = Integer.parseInt(InputArr[2]); // 총길이
		int a = Integer.parseInt(InputArr[0]); // a
		int b = Integer.parseInt(InputArr[1]); // b
		int back1 = 0; // 갈때 뒤돈 횟수
		int back2 = 0; // 올때 뒤돈 횟수
		int result = 0; // 최종 값
	
		/*
		 * 내가 움직일 수 있는 시간은 술래가 뒤돌아 있을 때이다. 총 움직인 시간 즉 술래가 뒤돌아있는 시간은 갈때 올때 총 2d만큼이다.
		 * 그 다음은 앞을 돌아보고있는 시간을 더해야 결과가 나온다.
		 * 앞을 돌아보고있는 시간을 갈때와 올때 술래가 앞을 보고있는 시간 기준이 다르기 때문에 구분하여 구해야한다.
		 * 술래에게 다가갈때 앞을보고있는 시간은 내가 움직인시간을 제외해야하기 때문에 d/a가 된다. (움직일 수 있는 시간은 a)
		 * 그렇다면 술래에게서 멀어질때 앞을보고있는 시간은 d/b가된다.
		 * 그럼 처음 나눠준 d/a에 술래가 앞을 돌아본 시간은 b이기 때문에 d/a*b를 해주면 술래에게 다가갈때 앞을 보고있는 시간이 계산된다.
		 * 이와 같은 맥락으로 d/b에 술래가 돌아본 시간 a를 곱해주면 d/b*a가 되고 술래에게서 멀어질때 앞을 보고있는 시간이 된다.
		 * 단 여기서 a와 b가 d보다 같거나 큰경우는 앞을보고있는 시간이 없기 때문에 0처리 해주어야한다.
		 */
		
		back1 = (d>a) ? d/a : 0;
		back2 = (d>b) ? d/b : 0;

		result = 2*d + back1*b + back2*a;
		
		System.out.println(result);
		
	}

 

 

갈때와 올때를 구분하여 변수처리 해주었고, d가 a나 b보다 큰 경우에는 몫을 0으로 처리하도록 하였다.

 

처음 생각한 코드에 비해 상당히 깔끔해진것을 확인할 수 있다.

 

성능 비교

 

첫번째 코드 성능

 

두번째 코드 성능

 

 

첫번째에 비해 두번째가 확연히 좋아진 부분을 확인할 수 있다.

 

위에 작성한 코드들이 정답이라는 것은 아니지만, 생각을 다르게 하였을때 볼 수 있는 변화를 기록하고 싶었다.

 

 

728x90