Post

[백준 - 15662] 톱니바퀴(2)

12100번: 톱니바퀴(2)
source-code

문제 분석

문제를 잘 이해하면 생각보다 어렵지 않은 문제 였다. 문제에서는 N극 S극의 톱니를 가진 n개의 톱니바퀴가 주어지고 각각의 톱니바퀴는 서로 맞닿아 있다. 이때 특정 톱니바퀴를 움직였을때 연쇄적으로 움직이는 톱니바퀴의 움직임을 구현하는 문제 이다.

톱니의 움직임

  • 톱니바퀴의 정보는 리스트에 저장된다 12시 방향의 톱니를 [0] 인덱스로하여 [7] 까지 총 8개의 리스트로 구성고 각 리스트의 값은 0[N],1[S] 이 저장된다 [아래 그림참조]
  • 움직이는 경우 : 맞닿아 있는 톱니의 극이 서로 다른 경우 [왼쪽 2 인덱스 와 오른쪽 6 인덱스의 값이 같은경우]
  • 움직이는 않는 경우 : 맞닿아 있는 톱니의 극이 서로 같은 [왼쪽 2 인덱스 와 오른쪽 6 인덱스의 값이 다른 경우]

톱니의 움직임에 따른 인덱스값 비교

tob-ni.jpg

실제 세상에서는 왼쪽 톱니바퀴를 회전 시키면 실시간으로 오른쪽 톱니바퀴도 회전 하게 된다. 하지만 이것을 코드로 구현하기란 쉽지 않다. 코드레벨에서 왼쪽 톱니바퀴를 움직인다고, 오른쪽 톱니바퀴가 실시간으로 움직이진 않는다. 먼저 왼쪽 톱니바퀴를 회전 후 그 데이터를 기반으로 오른쪽 톱니바퀴를 회전 시켜야 한다 정확히는 리스트의 인덱스를 기반으로 말이다.

예를들어 위의 그림에서 왼쪽 톱니바퀴를 시계방향으로 돌리면 [2] 인덱스의 값이 [3] 으로 이동하게 된다. 그럼 오른쪽 톱니바퀴의 [6] 인덱스와 비교해야할 대상은 왼쪽 톱니바퀴의 [2] 이 아니라 [3] 인덱스인 것이다. 반시계 방향도 원리는 동일하다. 이경우 오른쪽 톱니바퀴의 [6] 인덱스와 비교해야할 대상은 [1] 이 될것이다.

문제에서 움직일 톱니의 번호화 방향(시계, 반 시계)이 주어진다. 타겟이된 톱니바퀴를 기준으로 왼쪽, 오른쪽으로 재귀 로직은 전개하여 문제를 해결하였다.

1
2
3
4
5
6
7
8
9
10
11
12
13
for (rotateInfo rInfo : rotateInfos) {
  Gear gear = gears.get(rInfo.gearNum); // 선택된 톱니바퀴를 가져온다
  
  // 선택된 톱니바퀴 기준으로 왼쪽 바퀴대상 로직
  gear.rotate(rInfo.dir); // 선택된 톱니바퀴를 주어진 방향으로 회전.
  leftSide(rInfo.gearNum - 1, gear, rInfo.dir); // 현재 톱나바퀴 기준 왼쪽 톱니바퀴로 이동.
  
  gear.rotate(rInfo.dir * -1); // 재귀 로직을 끝내고 오른쪽 로직을 적용하기전 원복시키는 로직
  
  // 선택된 톱니바퀴 기준으로 오른쪽 바퀴대상 로직
  gear.rotate(rInfo.dir); // 선택된 톱니바퀴를 주어진 방향으로 회전.
  rightSide(rInfo.gearNum + 1, gear, rInfo.dir); // 현재 톱나바퀴 기준 오른쪽 톱니바퀴로 이동.
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
private void leftSide(int gearNum, Gear rightGear, int prevDir) {
  if (gearNum < 0) { return; } // 가장 왼쪽일 경우 return
  Gear gear = gears.get(gearNum);
  if (prevDir > 0) { // 이전바퀴가 시계방향으로 돌경우
    if (gear.slices.get(2).equals(rightGear.slices.get(7))) {
      //이전 바퀴는 이미 한번 돌았기 때문에 현재 gear의 [2]번 인덱스와 rightGear의 [7]번 인덱스를 비교
      //두개의 값이 같다면 현재 gear는 돌리지 않고 return
      return;
    } else {
      //두개의 값이 다르다면 현재바퀴는 반시계 (prevDir * -1) 방향으로 회전 후 다시 재귀 호출
      int curDir = prevDir * -1;
      gear.rotate(curDir);
      leftSide(gearNum - 1, gear, curDir);
    }
  } else { // 이전바퀴가 반 시계방향으로 돌경우
    if (gear.slices.get(2).equals(rightGear.slices.get(5))) {
      // 이전 바퀴는 이미 한번 돌았기 때문 현재 gear의 [2]번 인덱스와 rightGear의 [5]번 인덱스를 비교
      //두개의 값이 같다면 현재 gear는 돌리지 않고 return
      return;
    } else {
      //두개의 값이 다르다면 현재바퀴는 시계 (prevDir * -1) 방향으로 회전 후 다시 재귀 호출
      int curDir = prevDir * -1;
      gear.rotate(curDir);
      leftSide(gearNum - 1, gear, curDir);
    }
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
private void rightSide(int gearNum, Gear leftGear, int prevDir) {
  if (gearNum > gears.size() - 1) { return; }
  Gear gear = gears.get(gearNum);
  if (prevDir > 0) { // 이전바퀴가 시계방향으로 돌경우
    if (gear.slices.get(6).equals(leftGear.slices.get(3))) {
      //이전 바퀴는 이미 한번 돌았기 때문에 현재 gear의 [6]번 인덱스와 leftGear의 [3]번 인덱스를 비교
      //두개의 값이 같다면 현재 gear는 돌리지 않고 return
      return;
    } else {
      //두개의 값이 다르다면 현재 gear는 (prevDir * -1) 방향으로 회전 후 다시 재귀 호출
      int curDir = prevDir * -1;
      gear.rotate(curDir);
      rightSide(gearNum + 1, gear, curDir);
    }
  } else { // 이전바퀴가 반 시계방향으로 돌았다.
    if (gear.slices.get(6).equals(leftGear.slices.get(1))) {
      //이전 바퀴는 이미 한번 돌았기 때문에 현재 gear의 [6]번 인덱스와 leftGear의 [1]번 인덱스를 비교
      //두개의 값이 같다면 현재 gear는 돌리지 않고 return
      return;
    } else {
      //두개의 값이 다르다면 현재 gear는 (prevDir * -1) 방향으로 회전 후 다시 재귀 호출
      int curDir = prevDir * -1;
      gear.rotate(curDir);
      rightSide(gearNum + 1, gear, curDir);
    }
  }
}

회고

솔직히 문제를 바로 풀지는 못하고 푸는데 시간이좀 걸렸다. 잘 안되는 것중 하나가. 타자기에 손이 먼저 올라가는 것이다. 문제를 충분히, 이해하고, 분석한 후 코드를 작성해도 늦지 않는다.

This post is licensed under CC BY 4.0 by the author.