일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 | 28 |
- 이분 탐색
- bfs
- 그래프 탐색
- 패스트캠퍼스
- 백준 뒤집기 3
- 2493 백준
- 그래프이론
- 백준 1464
- boj 1464
- 최장길이바이토닉수열
- 최장증가수열
- parametric search
- 깊이 우선 탐색
- 뒤집기 3
- 결정문제
- 재귀
- union find
- 비트마스킹
- Lis
- 결정 문제
- 구현
- 1939백준
- 브루트포스
- 서로소 집합
- 분할정복
- 그래프 이론
- DP
- disjoint set
- 그래프탐색
- 이분탐색
- Today
- Total
알고리즘 문제풀이
[BOJ] 14891 번 - 톱니바퀴 본문
백준 14891번 - 톱니바퀴
시간제한 | 메모리 제한 | 제출 | 정답 | 맞은 사람 | 정답 비율 |
---|---|---|---|---|---|
2 초 | 512 MB | 17117 | 8784 | 6381 | 52.317% |
문제
총 8개의 톱니를 가지고 있는 톱니바퀴 4개가 아래 그림과 같이 일렬로 놓여져 있다. 또, 톱니는 N극 또는 S극 중 하나를 나타내고 있다. 톱니바퀴에는 번호가 매겨져 있는데, 가장 왼쪽 톱니바퀴가 1번, 그 오른쪽은 2번, 그 오른쪽은 3번, 가장 오른쪽 톱니바퀴는 4번이다.
이때, 톱니바퀴를 총 K번 회전시키려고 한다. 톱니바퀴의 회전은 한 칸을 기준으로 한다. 회전은 시계 방향과 반시계 방향이 있고, 아래 그림과 같이 회전한다.
톱니바퀴를 회전시키려면, 회전시킬 톱니바퀴와 회전시킬 방향을 결정해야 한다. 톱니바퀴가 회전할 때, 서로 맞닿은 극에 따라서 옆에 있는 톱니바퀴를 회전시킬 수도 있고, 회전시키지 않을 수도 있다. 톱니바퀴 A를 회전할 때, 그 옆에 있는 톱니바퀴 B와 서로 맞닿은 톱니의 극이 다르다면, B는 A가 회전한 방향과 반대방향으로 회전하게 된다. 예를 들어, 아래와 같은 경우를 살펴보자.
두 톱니바퀴의 맞닿은 부분은 초록색 점선으로 묶여있는 부분이다. 여기서, 3번 톱니바퀴를 반시계 방향으로 회전했다면, 4번 톱니바퀴는 시계 방향으로 회전하게 된다. 2번 톱니바퀴는 맞닿은 부분이 S극으로 서로 같기 때문에, 회전하지 않게 되고, 1번 톱니바퀴는 2번이 회전하지 않았기 때문에, 회전하지 않게 된다. 따라서, 아래 그림과 같은 모양을 만들게 된다.
위와 같은 상태에서 1번 톱니바퀴를 시계 방향으로 회전시키면, 2번 톱니바퀴가 반시계 방향으로 회전하게 되고, 2번이 회전하기 때문에, 3번도 동시에 시계 방향으로 회전하게 된다. 4번은 3번이 회전하지만, 맞닿은 극이 같기 때문에 회전하지 않는다. 따라서, 아래와 같은 상태가 된다.
톱니바퀴의 초기 상태와 톱니바퀴를 회전시킨 방법이 주어졌을 때, 최종 톱니바퀴의 상태를 구하는 프로그램을 작성하시오.
입력
첫째 줄에 1번 톱니바퀴의 상태, 둘째 줄에 2번 톱니바퀴의 상태, 셋째 줄에 3번 톱니바퀴의 상태, 넷째 줄에 4번 톱니바퀴의 상태가 주어진다. 상태는 8개의 정수로 이루어져 있고, 12시방향부터 시계방향 순서대로 주어진다. N극은 0, S극은 1로 나타나있다.
다섯째 줄에는 회전 횟수 K(1 ≤ K ≤ 100)가 주어진다. 다음 K개 줄에는 회전시킨 방법이 순서대로 주어진다. 각 방법은 두 개의 정수로 이루어져 있고, 첫 번째 정수는 회전시킨 톱니바퀴의 번호, 두 번째 정수는 방향이다. 방향이 1인 경우는 시계 방향이고, -1인 경우는 반시계 방향이다.
출력
총 K번 회전시킨 이후에 네 톱니바퀴의 점수의 합을 출력한다. 점수란 다음과 같이 계산한다.
- 1번 톱니바퀴의 12시방향이 N극이면 0점, S극이면 1점
- 2번 톱니바퀴의 12시방향이 N극이면 0점, S극이면 2점
- 3번 톱니바퀴의 12시방향이 N극이면 0점, S극이면 4점
- 4번 톱니바퀴의 12시방향이 N극이면 0점, S극이면 8점
예제 입력 1
10101111
01111101
11001110
00000010
2
3 -1
1 1
예제 출력 1
7
예제 입력 2
11111111
11111111
11111111
11111111
3
1 1
2 1
3 1
예제 출력 2
15
예제 입력 3
10001011
10000011
01011011
00111101
5
1 1
2 1
3 1
4 1
1 -1
예제 출력 3
6
예제 입력 4
10010011
01010011
11100011
01010101
8
1 1
2 1
3 1
4 1
1 -1
2 -1
3 -1
4 -1
예제 출력4
5
출처
www.acmicpc.net/problem/14891
알고리즘 분류
- 구현
- 시뮬레이션
접근 방법
문제를 정확하게 이해하지 못해, 헤맸던 문제이다.
문제를 처음봤을 때 특정 톱니바퀴를 회전한다면 인접한 톱니바퀴의 대응하는 극이 다르다면 인접한 톱니바퀴도 회전할 것이고
이것은 또한 초기에 움직였던 톱니바퀴와 극이 다른 상황을 연출할 수 있으므로, 또 회전시키는 식으로 구현을 했었다.
사실 이 부분에서 톱니바퀴가 무한히 회전하는 경우가 생기지 않을까 걱정했었는데, 내가 문제를 잘못 이해한 것을 뒤늦게 깨닫고는 수정하였다.
문제가 원하는 것은, 극이 다르다고 무조건 회전 시키는 것이 아닌 단순히 특정 톱니바퀴를 회전시킬 때 영향을 받는 인접 톱니바퀴들만 회전 시키는 것이다. 즉, 회전 할 톱니바퀴는 정해져 있는 상태에서 회전을 하는 것이다.
특정 톱니바퀴를 회전했을 때 영향을 받는 톱니바퀴들의 관계를 그래프로 표현할 수가 있다.
"a라는 톱니바퀴가 회전한다면 b라는 톱니바퀴가 회전한다" 를 edge로 표현해주면 그래프 탐색을 통해서 영향을 받는 모든 톱니바퀴들을 깔끔하게 움직일 수 있다.
특정 톱니 바퀴가 움직이기 바로 이전에 이러한 관계들을 그래프로 표현한 다음, 특정 톱니 바퀴를 회전 시키면서 그래프 탐색을 수행한다.
단, update() 함수에서 a-b가 연결되어있다면, a가 움직인 이후 a-b의 관계가 끊어질 수 있음을 상기해야한다.
그러면 update()를 톱니바퀴가 회전하는 순간 마다 해야 한다고 생각할 수도 있는데(ps.나도 그랬다..)
이미 움직인 톱니바퀴는 다시 회전하지 않으므로, 그런 case를 고려해 주지 않아도 된다.
소스 코드의 로직은 다음과 같다.
1. 톱니 바퀴들의 영향 관계를 is_move배열에 설정
2. 특정 톱니 바퀴를 회전시킨다.
2-1. 더 이상 인접 톱니 바퀴가 없으면 종료한다.
2-2. 인접 톱니 바퀴를 대상으로 1-2 과정을 반복한다.
소스 코드
#include <bits/stdc++.h>
using namespace std;
string wheel[5] ;
bool is_move[5][5] = {false, }; // is_move[i][j] : i번쨰 바퀴가 움직 였을 때, j번째 바퀴가 영향을 받는가?
// 연결 관계를 그래프로 표현한 것이다.
void update(){
// 변화되는 관계를 표현하는 배열 업데이트
memset(is_move, false, sizeof(is_move));
for(int i=1; i<=4; i++){
int left = i - 1, right = i + 1;
if( left >= 1 && wheel[left][2] != wheel[i][6] )
is_move[i][left] = true;
if( right <= 4 && wheel[right][6] != wheel[i][2] )
is_move[i][right] = true;
}
}
void rotate(int pos, int dir){
if( pos <= 0 || pos > 4 )
return ;
if( dir == 1 ){
// 시계 방향 회전
char tmp = wheel[pos][7];
for(int i=6; i>=0; i--){
wheel[pos][i+1] = wheel[pos][i];
}
wheel[pos][0] = tmp;
}
else{
char tmp = wheel[pos][0];
for(int i=1; i<=7; i++){
wheel[pos][i-1] = wheel[pos][i];
}
wheel[pos][7] = tmp;
}
for(int i=1; i<=4; i++){
if( is_move[pos][i] ){
is_move[pos][i] = false;
is_move[i][pos] = false;
rotate(i, -1 * dir);
}
}
}
int main(void){
for(int i=1; i<=4; i++)
cin >> wheel[i];
int K;
cin >> K;
while(K--){
int to_move, direction;
cin >> to_move >> direction;
update();
// 회전 진행
rotate(to_move,direction);
}
int answer = 0;
for(int i=1; i<=4; i++){
if( wheel[i][0] == '1' )
answer += pow(2,i-1);
}
cout << answer;
return 0;
}
'자료구조 + 알고리즘 > [BOJ]' 카테고리의 다른 글
[BOJ] 14728 번 - 벼락치기 (0) | 2021.01.19 |
---|---|
[BOJ] 2098 - 외판원 순회 (0) | 2021.01.17 |
[BOJ] 13460번 - 구슬 탈출 2 (0) | 2021.01.07 |
[BOJ] 1005번 - ACM Craft (0) | 2020.12.30 |
[BOJ] 2056번 - 작업 (0) | 2020.12.30 |