알고리즘

BOJ: 톱니바퀴(2)(15662) [C++]

avocado8 2024. 3. 28. 15:40

https://www.acmicpc.net/problem/15662

 

15662번: 톱니바퀴 (2)

총 8개의 톱니를 가지고 있는 톱니바퀴 T개가 아래 그림과 같이 일렬로 놓여져 있다. 또, 톱니는 N극 또는 S극 중 하나를 나타내고 있다. 톱니바퀴에는 번호가 매겨져 있는데, 가장 왼쪽 톱니바퀴

www.acmicpc.net

 

고민을 길게 해봤으나 결국 마감일까지 풀지 못하고 제출실패, ,

실패한 주된 이유는 각 톱니바퀴의 회전 체크 여부(아래 코드에서 visited 배열)를 표시해주지 않았기 때문이었다.

회전여부는 회전당 한 번만 결정된다. 그리고 톱니바퀴가 돌아가는 데는 순서가 없다.

체크 여부를 표시해주지 않으면 이미 돌린 톱니에 의해 극을 다시 체크하여 실제 회전 상태와 다른 동작이 일어나는 문제가 발생한다. 이 때문에 테케가 뭐는 되고 뭐는 안 되고 했던 듯

사실 중복체크를 해야한다는 발상자체는 했으나... bool 자료형으로 처리해보려 했다가 실패

 

인접한 톱니를 확인할 때, 왼쪽으로 인접한 것과 오른쪽으로 인접한 것에서 각 동작이 다른 것에 유의

#include <iostream>
#include <vector>
#include <string>

using namespace std;

vector<string> li; //톱니바퀴를 저장
//li[0] : 1번째 톱니바퀴, li[1]: 2번째 톱니바퀴, ...

vector<pair<int, int>> stack; //돌려야하는 톱니바퀴 인덱스와 방향
vector<int>visited; //검사한 톱니바퀴를 다시 검사하면 올바르지 않은 결과가 나오므로 검사여부 표시

void check_rotation(int current_num, int current_dir) {
	//매개변수: 현재 기준이 되는 바퀴의 인덱스와 회전 방향

	//왼쪽에 있는 톱니바퀴가 회전하는지 검사
	if (current_num - 1 >= 0 && visited[current_num - 1] == 0) { //왼쪽에 톱니바퀴가 있고, 아직 검사하지 않은 상태여야 회전가능
		//나의 6번째 톱니와 내 왼쪽의 2번째 톱니 비교
		if (li[current_num][6] != li[current_num-1][2]) { //맞물린 톱니가 다른 극이면 회전해야 함
			stack.push_back(make_pair(current_num - 1, current_dir * -1)); //왼쪽 톱니바퀴를 반대 방향으로 회전
			visited[current_num - 1] = 1; //검사했음을 표시
			check_rotation(current_num - 1, current_dir * -1); //왼쪽 톱니바퀴에 대해 재귀호출
		}
	}
	//오른쪽에 있는 톱니바퀴가 회전하는지 검사
	if (current_num + 1  < li.size() && visited[current_num + 1] == 0) { //오른쪽에 톱니바퀴가 있고, 아직 검사하지 않은 상태여야 회전가능
		//나의 2번째 톱니와 내 왼쪽의 6번째 톱니 비교
		if (li[current_num][2] != li[current_num+1][6]) { //맞물린 톱니가 다른 극이면 회전해야 함
			stack.push_back(make_pair(current_num + 1, current_dir * -1)); //오른쪽 톱니바퀴를 반대 방향으로 회전
			visited[current_num + 1] = 1; //검사했음을 표시
			check_rotation(current_num + 1, current_dir * -1); //오른쪽 톱니바퀴에 대해 재귀호출
		}
	}
}

int main() {
	int t; //톱니바퀴의 개수
	cin >> t;
	li.resize(t); //t만큼 li 할당
	for (int i = 0; i < t; i++) { //톱니바퀴 정보 입력받음
		cin >> li[i];
	}
	int k; //회전횟수
	cin >> k;
	for (int i = 0; i < k; i++) { //첫번째로 돌려야 할 톱니바퀴에 대해 바로 돌리기
		int num, direction; //회전한 톱니바퀴 인덱스, 방향
		cin >> num >> direction;
		stack.clear(); //매 회전마다 새로 받아야 하므로 스택 초기화
		stack.push_back(make_pair(num - 1, direction)); //회전 정보를 저장. 톱니바퀴 인덱스는 톱니바퀴번호 - 1
		visited.assign(t, 0); //방문 여부 표시를 위한 배열 할당
		visited[num - 1] = 1; //첫번째 톱니바퀴에 방문 표시
		
		//돌릴 톱니바퀴 체크
		check_rotation(num - 1, direction);

		//돌려야 하는 톱니바퀴를 모두 체크한 이후
		while (!stack.empty()) {
			pair<int, int> top = stack.back(); //회전 정보를 스택에서 꺼냄
			stack.pop_back(); //스택에서 삭제
			int tmp_num = top.first; //현재 돌려야 할 톱니바퀴 인덱스
			int tmp_dir = top.second; //현재 돌려야할 톱니바퀴 방향
			if (tmp_dir == 1) { //시계방향이면
				li[tmp_num] = li[tmp_num].back() + li[tmp_num].substr(0, 7); //맨 뒤였던 것을 맨 앞으로 하고 나머지 붙이기
			}
			else { //반시계 방향이면
				li[tmp_num] = li[tmp_num].substr(1) + li[tmp_num][0]; //1번째~7번째 인덱스를 맨앞으로 옮기고 0번째 인덱스를 맨 뒤로 붙이기
			}
		}
	}
	//12시 방향(0번인덱스)이 S극인 톱니바퀴 개수 세기
	int cnt = 0;
	for (int i = 0; i < t; i++) {
		if (li[i][0] == '1') {
			cnt++;
		}
	}
	cout << cnt;
	return 0;
}

 

반복문을 사용해 각 회전마다 로직을 적용해준다.

회전 정보 입력받고, visited를 초기화해준 뒤 회전 여부를 체크하는 함수를 실행한다.

체크가 끝나면 실제로 돌려줘야 하니까 while문을 실행. 이때 stack 배열에는 회전이 결정된 톱니바퀴의 정보가 들어있다.

그 정보들을 꺼내서 각 톱니바퀴를 맞는 방향으로 돌려주면 된다.