문제 링크


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

 

5373번: 큐빙

문제 루빅스 큐브는 삼차원 퍼즐이다. 보통 루빅스 큐브는 3×3×3개의 작은 정육면체로 이루어져 있다. 퍼즐을 풀려면 각 면에 있는 아홉 개의 작은 정육면체의 색이 동일해야 한다. 큐브는 각 면

www.acmicpc.net

 

문제 접근


모든 면에 대한 색상을 큐브를 돌릴 때 마다 일일히 바꿔주는 것은 고려해야할 것이 너무 많다고 생각했습니다. 제가 접근한 방법은 3x3x3의 큐브의 각 단위에 해당하는 '작은 큐브'를 정의하는 것이었습니다. '작은 큐브'는 쉽게 말해서 정육면체이며, 6가지 면에 대한 색상 정보를 담습니다. 

typedef struct cuve {
	char top, front, bot, back, left, right;
};

위의 작은 큐브를 통해서 3x3x3의 큐브를 만들어 줍니다.

cuve cuv[3][3][3];

그리고 모든 작은 큐브에 대하여 동일하게 초기화 해줍니다.

void init_cuve() {
	for (int h = 0; h < 3; ++h) {
		for (int y = 0; y < 3; ++y) {
			for (int x = 0; x < 3; ++x) {
				cuv[h][y][x].top = 'w';
				cuv[h][y][x].bot = 'y';
				cuv[h][y][x].front = 'r';
				cuv[h][y][x].back = 'o';
				cuv[h][y][x].left = 'g';
				cuv[h][y][x].right = 'b';
			}
		}
	}
}

각 작은 큐브는 표면에 노출된 면만 고정적으로 노출되므로 보이지 않는 면을 초기화 한다해도 문제 없습니다.

 

이제 큐브 정의가 끝났습니다. 이제 면(side)과 방향(direction)을 입력받아서 큐브를 회전하는 함수를 짜봅시다.

회전의 정의는 큐브의 회전은 '각 면을 정면에서 바라보았을 때의 시계/반시계 방향'으로 이루어진다고 나와있습니다.

그 말을 각 면에 대해서 그림을 그려본다면, 앞(F)면을 시계방향으로 회전하는 것과, 뒷(B)면을 반시계방향으로 회전하는 것방향이 같음을 알 수 있습니다. 또한 (h, y, x)의 좌표계에서 바라보면 앞면과 뒷면은 h좌표가 고정이고, (x, y)좌표만 움직임을 알 수 있습니다. 이를 정리하면 아래와 같습니다.

cuve[3][3][3]

- 윗면(U)/아랫면(D) : h고정(0 또는 2) xy가변, (방향관점) 윗면 시계방향 = 아랫면 반시계방향

- 앞면(F)/뒷면(B) : y고정(0 또는 2), hx가변, (방향관점) 앞면 시계방향 = 뒷면 반시계방향

- 왼쪽(L)/오른쪽(R) : x고정(0 또는 2), hy가변, (방향관점) 왼쪽 시계방향 = 오른쪽 반시계방향

 

이제 어떤 면을 선택하였을때 어떻게 좌표 접근을 해야할지 해결하였습니다.

이제 시계/반시계 방향으로 회전하는 것을 알아봅시다.

(h, y, x)로 좌표를 표현하면 위 사진과 같습니다. 윗면을 시계방향(h고정, xy가변)으로 회전하면

- (0, 0, 0) -> (0, 0, 2)로 이동합니다.

- (0, 1, 0) -> (0, 0, 1)로 이동합니다. y가 1증가 -> x가 1감소

- (0, 1, 1) -> (0, 1, 1)로 이동합니다. x가 1증가 -> y가 1증가

규칙이 보이시나요? 코드로 표현하면 아래와 같습니다. (색상은 밑에서 회전하겠습니다)

new_cuve[h][x][2 - y] = cuv[h][y][x];

위의 방법으로 다른 모든 면과 방향에 대해서 정의해줍니다.

 

윗면 시계 || 아랫면 반시계 = new_cuve[h][x][2 - y] = cuv[h][y][x];

윗면 반시계 || 아랫면 시계 = new_cuve[h][2 - x][y] = cuv[h][y][x];

앞면 시계 || 뒷면 반시계 = new_cuve[x][y][2 - h] = cuv[h][y][x];

앞면 반시계 || 뒷면 시계 = new_cuve[2 - x][y][h] = cuv[h][y][x];

오른쪽 시계 || 왼쪽 반시계 = new_cuve[2 - y][h][x] = cuv[h][y][x];

오른쪽 반시계 || 왼쪽 시계 = new_cuve[y][2 - h][x] = cuv[h][y][x];

 

자 이제 방향과 좌표 정리가 끝났습니다. 이제 남은건 회전하면서 작은 큐브의 색상도 회전해주는 일입니다.

이 부분은 각 면마다 작은 큐브가 회전하는 방향이 달라서 규칙을 찾는데 실패했습니다. 하지만 매우 간단한 코드이기 때문에 괜찮습니다..

 

윗면 시계 || 아랫면 반시계 = back -> right -> front -> left -> back같은 순으로 4가지 색상을 옮겨주기만 하면 됩니다!

이 부분에 더 좋은 접근 방법이 있다면 다른 글을 참고해주세요. 저는 생각이 나지 않았습니다..

 

윗면 반시계 || 아랫면 시계 = back -> left -> front -> right -> back

앞면 시계 || 뒷면 반시계 = top -> right -> bot -> left -> top

앞면 반시계 || 뒷면 시계 = top -> left -> bot -> right -> top

오른쪽 시계 || 왼쪽 반시계 = top -> back -> bot -> front -> top

오른쪽 반시계 || 왼쪽 시계 = top -> front -> bot -> back -> top

 

회전 소스 코드는 아래와 같습니다.

// 큐브 from에서 큐브 to로 값을 복사
void copy_cuve(cuve from[][3][3], cuve to[][3][3]) {
	int h, y, x;
	for (h = 0; h < 3; ++h)
		for (y = 0; y < 3; ++y)
			for (x = 0; x < 3; ++x)
				to[h][y][x] = from[h][y][x];
}

// 명령어를 입력받고 면을 회전하는 코드 (side: 면, direction: 시계/반시계)
void rotate(char side, char direction) {
	char temp;
	int h, y, x;
	cuve new_cuve[3][3][3];
	copy_cuve(cuv, new_cuve);
	if (side == 'U' || side == 'D') { // 윗면 또는 아랫면인 경우 (h고정, yx가변)
		h = (side == 'U' ? 0 : 2); // 높이 고정
		if ((direction == '+' && side == 'U') ||
			(direction == '-' && side == 'D')) {
			// 윗면 시계, 바닥면 반시계
			for (y = 0; y < 3; ++y) {
				for (x = 0; x < 3; ++x) {
					// back -> right -> front -> left -> back
					temp = cuv[h][y][x].left;
					cuv[h][y][x].left = cuv[h][y][x].front;
					cuv[h][y][x].front = cuv[h][y][x].right;
					cuv[h][y][x].right = cuv[h][y][x].back;
					cuv[h][y][x].back = temp;

					new_cuve[h][x][2 - y] = cuv[h][y][x];
				}
			}
		}
		else { // 윗면 반시계, 바닥면 시계
			for (y = 0; y < 3; ++y) {
				for (x = 0; x < 3; ++x) {
					// back -> left -> front -> right -> back
					temp = cuv[h][y][x].right;
					cuv[h][y][x].right = cuv[h][y][x].front;
					cuv[h][y][x].front = cuv[h][y][x].left;
					cuv[h][y][x].left = cuv[h][y][x].back;
					cuv[h][y][x].back = temp;

					new_cuve[h][2 - x][y] = cuv[h][y][x];
				}
			}
		}
	}
	else if (side == 'F' || side == 'B') { // 앞면 또는 뒷면인 경우 (y고정, hx가변)
		y = (side == 'F' ? 2 : 0); // y 고정 (앞면: 2, 뒷면: 0)
		if ((direction == '+' && side == 'F') ||
			(direction == '-' && side == 'B')) { // 앞면 시계, 뒷면 반시계
			for (h = 0; h < 3; ++h) {
				for (x = 0; x < 3; ++x) {
					// top -> right -> bot -> left -> top
					temp = cuv[h][y][x].left;
					cuv[h][y][x].left = cuv[h][y][x].bot;
					cuv[h][y][x].bot = cuv[h][y][x].right;
					cuv[h][y][x].right = cuv[h][y][x].top;
					cuv[h][y][x].top = temp;

					new_cuve[x][y][2 - h] = cuv[h][y][x];
				}
			}
		}
		else { // 앞면 반시계, 뒷면 시계
			for (h = 0; h < 3; ++h) {
				for (x = 0; x < 3; ++x) {
					// top -> left -> bot -> right -> top
					temp = cuv[h][y][x].right;
					cuv[h][y][x].right = cuv[h][y][x].bot;
					cuv[h][y][x].bot = cuv[h][y][x].left;
					cuv[h][y][x].left = cuv[h][y][x].top;
					cuv[h][y][x].top = temp;

					new_cuve[2 - x][y][h] = cuv[h][y][x];
				}
			}
		}
	}
	else if (side == 'L' || side == 'R') { // 왼쪽 또는 오른쪽 (x고정, hy가변) 
		x = (side == 'L' ? 0 : 2); // x 고정 (왼쪽: 0, 오른쪽: 2)
		if ((direction == '+' && side == 'R') ||
			(direction == '-' && side == 'L')) { 
			// 오른쪽 시계, 왼쪽 반시계
			for (h = 0; h < 3; ++h) {
				for (y = 0; y < 3; ++y) {
					// top -> back -> bot -> front -> top
					temp = cuv[h][y][x].front;
					cuv[h][y][x].front = cuv[h][y][x].bot;
					cuv[h][y][x].bot = cuv[h][y][x].back;
					cuv[h][y][x].back = cuv[h][y][x].top;
					cuv[h][y][x].top = temp;

					new_cuve[2 - y][h][x] = cuv[h][y][x];
				}
			}
		}
		else { // 오른쪽 반시계, 왼쪽 시계
			for (h = 0; h < 3; ++h) {
				for (y = 0; y < 3; ++y) {
					// top -> front -> bot -> back -> top
					temp = cuv[h][y][x].back;
					cuv[h][y][x].back = cuv[h][y][x].bot;
					cuv[h][y][x].bot = cuv[h][y][x].front;
					cuv[h][y][x].front = cuv[h][y][x].top;
					cuv[h][y][x].top = temp;

					new_cuve[y][2 - h][x] = cuv[h][y][x];
				}
			}
		}
	}
	copy_cuve(new_cuve, cuv); // new -> cuv로 copy
}

 

소스 코드


#include <vector>
#include <string>
#include <iostream>
#include <algorithm>
using namespace std;
typedef struct cuve {
	char top, front, bot, back, left, right;
};
cuve cuv[3][3][3];	

void init_cuve() {
	for (int h = 0; h < 3; ++h) {
		for (int y = 0; y < 3; ++y) {
			for (int x = 0; x < 3; ++x) {
				cuv[h][y][x].top = 'w';
				cuv[h][y][x].bot = 'y';
				cuv[h][y][x].front = 'r';
				cuv[h][y][x].back = 'o';
				cuv[h][y][x].left = 'g';
				cuv[h][y][x].right = 'b';
			}
		}
	}
}

// 큐브 from에서 큐브 to로 값을 복사
void copy_cuve(cuve from[][3][3], cuve to[][3][3]) {
	int h, y, x;
	for (h = 0; h < 3; ++h)
		for (y = 0; y < 3; ++y)
			for (x = 0; x < 3; ++x)
				to[h][y][x] = from[h][y][x];
}

// 명령어를 입력받고 면을 회전하는 코드 (side: 면, direction: 시계/반시계)
void rotate(char side, char direction) {
	char temp;
	int h, y, x;
	cuve new_cuve[3][3][3];
	copy_cuve(cuv, new_cuve);
	if (side == 'U' || side == 'D') { // 윗면 또는 아랫면인 경우 (h고정, yx가변)
		h = (side == 'U' ? 0 : 2); // 높이 고정
		if ((direction == '+' && side == 'U') ||
			(direction == '-' && side == 'D')) {
			// 윗면 시계, 바닥면 반시계
			for (y = 0; y < 3; ++y) {
				for (x = 0; x < 3; ++x) {
					// back -> right -> front -> left -> back
					temp = cuv[h][y][x].left;
					cuv[h][y][x].left = cuv[h][y][x].front;
					cuv[h][y][x].front = cuv[h][y][x].right;
					cuv[h][y][x].right = cuv[h][y][x].back;
					cuv[h][y][x].back = temp;

					new_cuve[h][x][2 - y] = cuv[h][y][x];
				}
			}
		}
		else { // 윗면 반시계, 바닥면 시계
			for (y = 0; y < 3; ++y) {
				for (x = 0; x < 3; ++x) {
					// back -> left -> front -> right -> back
					temp = cuv[h][y][x].right;
					cuv[h][y][x].right = cuv[h][y][x].front;
					cuv[h][y][x].front = cuv[h][y][x].left;
					cuv[h][y][x].left = cuv[h][y][x].back;
					cuv[h][y][x].back = temp;

					new_cuve[h][2 - x][y] = cuv[h][y][x];
				}
			}
		}
	}
	else if (side == 'F' || side == 'B') { // 앞면 또는 뒷면인 경우 (y고정, hx가변)
		y = (side == 'F' ? 2 : 0); // y 고정 (앞면: 2, 뒷면: 0)
		if ((direction == '+' && side == 'F') ||
			(direction == '-' && side == 'B')) { // 앞면 시계, 뒷면 반시계
			for (h = 0; h < 3; ++h) {
				for (x = 0; x < 3; ++x) {
					// top -> right -> bot -> left -> top
					temp = cuv[h][y][x].left;
					cuv[h][y][x].left = cuv[h][y][x].bot;
					cuv[h][y][x].bot = cuv[h][y][x].right;
					cuv[h][y][x].right = cuv[h][y][x].top;
					cuv[h][y][x].top = temp;

					new_cuve[x][y][2 - h] = cuv[h][y][x];
				}
			}
		}
		else { // 앞면 반시계, 뒷면 시계
			for (h = 0; h < 3; ++h) {
				for (x = 0; x < 3; ++x) {
					// top -> left -> bot -> right -> top
					temp = cuv[h][y][x].right;
					cuv[h][y][x].right = cuv[h][y][x].bot;
					cuv[h][y][x].bot = cuv[h][y][x].left;
					cuv[h][y][x].left = cuv[h][y][x].top;
					cuv[h][y][x].top = temp;

					new_cuve[2 - x][y][h] = cuv[h][y][x];
				}
			}
		}
	}
	else if (side == 'L' || side == 'R') { // 왼쪽 또는 오른쪽 (x고정, hy가변) 
		x = (side == 'L' ? 0 : 2); // x 고정 (왼쪽: 0, 오른쪽: 2)
		if ((direction == '+' && side == 'R') ||
			(direction == '-' && side == 'L')) { 
			// 오른쪽 시계, 왼쪽 반시계
			for (h = 0; h < 3; ++h) {
				for (y = 0; y < 3; ++y) {
					// top -> back -> bot -> front -> top
					temp = cuv[h][y][x].front;
					cuv[h][y][x].front = cuv[h][y][x].bot;
					cuv[h][y][x].bot = cuv[h][y][x].back;
					cuv[h][y][x].back = cuv[h][y][x].top;
					cuv[h][y][x].top = temp;

					new_cuve[2 - y][h][x] = cuv[h][y][x];
				}
			}
		}
		else { // 오른쪽 반시계, 왼쪽 시계
			for (h = 0; h < 3; ++h) {
				for (y = 0; y < 3; ++y) {
					// top -> front -> bot -> back -> top
					temp = cuv[h][y][x].back;
					cuv[h][y][x].back = cuv[h][y][x].bot;
					cuv[h][y][x].bot = cuv[h][y][x].front;
					cuv[h][y][x].front = cuv[h][y][x].top;
					cuv[h][y][x].top = temp;

					new_cuve[y][2 - h][x] = cuv[h][y][x];
				}
			}
		}
	}
	copy_cuve(new_cuve, cuv); // new -> cuv로 copy
}

void print_cuve() {
	for (int y = 0; y < 3; ++y) {
		for (int x = 0; x < 3; ++x) {
			cout << cuv[0][y][x].top;
		}
		cout << "\n";
	}
}

int main() {
	ios_base::sync_with_stdio(false); cin.tie(NULL); cout.tie(NULL);
	int T, N;
	char side, direction;
	cin >> T;
	for (int t = 0; t < T; ++t) {
		init_cuve();
		cin >> N;
		for (int i = 0; i < N; ++i) {
			cin >> side >> direction;
			rotate(side, direction);
		}
		print_cuve();
	}
	return 0;
}

개발 환경 : VS2019

질문, 지적 환영합니다!

'알고리즘 > 백준' 카테고리의 다른 글

[C++] 백준 17837 새로운 게임 2  (0) 2020.05.29
[C++] 백준 1520 내리막길  (0) 2020.05.26
[C++] 백준 13460 구슬 탈출 2  (0) 2020.05.26
[C++] 백준 15684 사다리 조작  (0) 2020.05.25
[C++] 백준 14891 톱니바퀴  (0) 2020.05.25

문제 링크


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

 

14890번: 경사로

첫째 줄에 N (2 ≤ N ≤ 100)과 L (1 ≤ L ≤ N)이 주어진다. 둘째 줄부터 N개의 줄에 지도가 주어진다. 각 칸의 높이는 10보다 작거나 같은 자연수이다.

www.acmicpc.net

 

 

문제 접근


점 (i, j)와 (i, j + 1)의 비교의 경우의 수는 다음과 같다.

1. 높이의 차가 2 이상인 경우

2. 같은 높이인 경우

3. 차가 1일때, 오르막인 경우

4. 차가 1일때, 내리막인 경우

 

높이의 차가 2인 경우, 해당 길은 지날 수 없다.

까다로운 부분은 오르막, 내리막의 경우인데, 낮은 지대에 경사로의 크기 L을 수용할 만한 공간이 있음을 체크해야한다.

 

- (i, j + 1)이 오르막인 경우, (i, j)가 더 낮은 지대이므로 (i, j)부터 뒤로 가면서 낮은 지대의 갯수가 L보다 크거나 같으면 이동 가능

 

- (i, j + 1)이 내리막인 경우, (i, j + 1)이 더 같은 지대이므로, (i, j + 1)부터 앞으로 가면서 낮은 지대의 갯수가 L보다 크거나 같으면 이동 가능

 

또 다른 중요한 점은 내리막과 오르막이 연달아 있는 경우에서 경사로를 겹쳐서 놓는 경우는 허가되지 않는다는 것이다. 따라서 visit 배열을 통해 경사로가 놓인 위치를 표시해줘야한다.

 

 

소스 코드


#include <cstring>
#include <iostream>
#include <algorithm>
#define MAX 100
using namespace std;
int N, L, map[MAX][MAX], visit[MAX];

void input() {
	cin >> N >> L;
	for (int i = 0; i < N; ++i)
		for (int j = 0; j < N; ++j)
			cin >> map[i][j];
}

int solve() {
	int differ, ret = 0, low_area = 0;
	for (int i = 0; i < N; ++i) {
		memset(visit, 0, sizeof(visit));
		// 가로 검사
		for (int j = 1; j < N; ++j) {
			differ = map[i][j - 1] - map[i][j];
			if (abs(differ) > 1) break; // 차이가 2 이상이면 갈 수 있는 길이 아님
			
			// 오르막인 경우
			if (differ < 0) {
				low_area = 0;
				for (int l = j - 1; l >= 0; --l) { // j가 높은 지대이므로 j-1이전을 카운트
					if (visit[l] || map[i][l] != map[i][j - 1]) break;
					++low_area;
				}
				if (low_area >= L) { // 다리놓을 공간이 충분하므로 L개만큼 방문 표시하자
					low_area = L;
					for (int l = j - 1; l >= 0 && low_area > 0; --l, --low_area)
						visit[l] = 1;
				}
				else {
					break;
				}
			}

			// 내리막인 경우
			else if (differ > 0) { // j부터 낮은 지대이다.
				low_area = 0;
				for (int l = j; l < N; ++l) {
					if (visit[l] || map[i][l] != map[i][j]) break;
					++low_area;
				}
				if (low_area >= L) { // 다리놓을 공간이 충분하므로 L개만큼 방문 표시하자
					low_area = L;
					for (int l = j; l < N && low_area > 0; ++l, --low_area)
						visit[l] = 1;
				}
				else break;
			}
			// break되지 않고 여기까지왔다면 갈 수 있는 길임
			if (j == N - 1) ++ret;
		}
		memset(visit, 0, sizeof(visit));
		// 세로 검사
		for (int j = 1; j < N; ++j) {
			differ = map[j - 1][i] - map[j][i];
			if (abs(differ) > 1) break; // 차이가 2 이상이면 갈 수 있는 길이 아님

			// 오르막인 경우
			if (differ < 0) {
				low_area = 0;
				for (int l = j - 1; l >= 0; --l) { // j가 오르막이므로 j-1부터 본다.
					if (visit[l] || map[l][i] != map[j - 1][i]) break;
					++low_area;
				}
				if (low_area >= L) { // 다리놓을 공간이 충분하므로 L개만큼 방문 표시하자
					low_area = L;
					for (int l = j - 1; l >= 0 && low_area > 0; --l, --low_area)
						visit[l] = 1;
				}
				else break;
			}

			// 내리막인 경우
			else if (differ > 0) {
				low_area = 0;
				for (int l = j; l < N; ++l) { // j부터 낮은 지대임
					if (visit[l] || map[l][i] != map[j][i]) break;
					++low_area;
				}
				if (low_area >= L) { // 다리놓을 공간이 충분하므로 L개만큼 방문 표시하자
					low_area = L;
					for (int l = j; l < N && low_area > 0; ++l, --low_area)
						visit[l] = 1;
				}
				else break;
			}
			// break되지 않고 여기까지왔다면 갈 수 있는 길임
			if (j == N - 1) ++ret;
		}
	}
	return ret;
}

int main() {
	ios_base::sync_with_stdio(false); cin.tie(NULL); cout.tie(NULL);
	input();
	cout << solve();
	return 0;
}

개발 환경 : VS2019

질문, 지적 환영합니다.

'알고리즘 > 백준' 카테고리의 다른 글

[C++] 백준 15684 사다리 조작  (0) 2020.05.25
[C++] 백준 14891 톱니바퀴  (0) 2020.05.25
[C++] 백준 15683 감시  (0) 2020.05.20
[C++] 백준 14500 테트로미노  (0) 2020.05.20
[C++] 백준 2206 벽 부수고 이동하기  (0) 2020.05.19

문제 링크


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

 

15683번: 감시

스타트링크의 사무실은 1×1크기의 정사각형으로 나누어져 있는 N×M 크기의 직사각형으로 나타낼 수 있다. 사무실에는 총 K개의 CCTV가 설치되어져 있는데, CCTV는 5가지 종류가 있다. 각 CCTV가 감��

www.acmicpc.net

 

 

접근 방법


아래와 같은 순서로 문제를 접근하였다.

1. CCTV의 타입(1~5), 좌표, 방향을 cctv 백터에 push

2. 모든 CCTV의 방향 조합 문제로 변형 -> 백트래킹 시도 (CCTV 타입도 고려해야함)

3. 백트래킹하며 CCTV의 타입과 방향, 좌표를 selected 벡터에 push

4. selected 크기가 cctv 전체 갯수 도달시 현재까지 선택된 CCTV 타입, 방향, 좌표 정보를 가지고

map에 감시 영역을 채운다.

5. 최소 사각지대의 개수를 반환한다.

 

단순히 따지자면 CCTV의 방향의 조합 문제인데, CCTV마다 한 번에 감시할 수 있는 방향이 다르기 때문에 CCTV 타입도 고려하면서 진행하였다.

 

방향에 대한 접근은 위와 같이 하였다. 타입 1 CCTV는 위와 같은 방향 접근으로 하며, 다른 타입도 위의 방향을 기반으로 정의하였다.

 

2번 타입은 위 사진과 같이 접근하였다. 정리하면 a번째 방향 + a+2번째 방향이다.

 

3번은 위와 같다. 정리하면 a번째 방향 + a+1번째 방향이다.

 

4번은 위와 같다. 정리하면 a번째 방향 + a-1번째 방향 + a + 1번째 방향이다.

 

5번은 모든 방향이다.

 

cctv의 정보를 담기 위해 구조체를 정의하였는데, CCTV의 타입, 방향, 좌표를 저장하였다.

각 CCTV 타입마다 방향의 개수가 다르므로 num_direct를 선언하였고, 방향에 대한 정보는 DIRECTION에 담았다.

typedef struct cctv {
	int type;
	int direct;
	int y, x;
};
int num_direct[NUM_TYPE] = { 0, 4, 2, 4, 4, 1 }; // 1번: 4개, 2번: 2개, 3번: 4개, 4번: 2개, 5번: 1개
const coord DIRECTION[4] = { {-1, 0}, {0, 1}, {1, 0}, {0, -1} };

 

위의 방향 접근법으로 해당 방향 라인을 체크해주는 함수를 정의하였다.

(여기서 tmp는 map의 복사본입니다.)

void fill_line(vector<vector<int>> &tmp, int sy, int sx, int direct) {
	while (isRanged(sy, sx) && map[sy][sx] != 6) {
		tmp[sy][sx] = 9;
		sy += DIRECTION[direct].y;
		sx += DIRECTION[direct].x;
	}
}

위의 함수는 tmp라는 2차원 배열에 시작 위치 (sy,sx)를 기준으로 direct 방향으로 모든 영역을 9로 표시한다. (벽을 만나면 멈춤)

위 함수를 이용하여 cctv의 타입에 따라서 map을 칠해주는 함수를 정의하였다. 내용은 아래와 같다.

void fill_map(vector<vector<int>> &tmp, cctv c) {
	if (c.type == 1) {
		fill_line(tmp, c.y, c.x, c.direct);
	}
	else if (c.type == 2) {
		fill_line(tmp, c.y, c.x, c.direct);
		fill_line(tmp, c.y, c.x, c.direct + 2);
	}
	else if (c.type == 3) {
		fill_line(tmp, c.y, c.x, c.direct);
		fill_line(tmp, c.y, c.x, (c.direct + 1) % 4);
	}
	else if (c.type == 4) {
		fill_line(tmp, c.y, c.x, c.direct);
		fill_line(tmp, c.y, c.x, (c.direct == 0 ? 3 : c.direct - 1));
		fill_line(tmp, c.y, c.x, (c.direct + 1) % 4);
	}
	else {
		for (int direct = 0; direct < 4; ++direct)
			fill_line(tmp, c.y, c.x, direct);
	}
}

위 함수는 tmp라는 2차원 배열을 받아서 cctv type c와 방향에 따라서 map에 감시 구역을 표시해주는 함수이다.

 

소스 코드


#include <vector>
#include <climits>
#include <iostream>
#include <algorithm>
#define MAX 8
#define NUM_TYPE 6
using namespace std;
typedef struct cctv {
	int type;
	int direct;
	int y, x;
};
typedef struct coord {
	int y, x;
};
int N, M;
vector<vector<int>> map(MAX, vector<int>(MAX, 0)), tmp(MAX, vector<int>(MAX, 0));
vector<cctv> cctv_list, selected;
int num_direct[NUM_TYPE] = { 0, 4, 2, 4, 4, 1 }; // 1번: 4개, 2번: 2개, 3번: 4개, 4번: 2개, 5번: 1개
const coord DIRECTION[4] = { {-1, 0}, {0, 1}, {1, 0}, {0, -1} };

void input() {
	cin >> N >> M;
	for (int i = 0; i < N; ++i) {
		for (int j = 0; j < M; ++j) {
			cin >> map[i][j];
			if (map[i][j] != 0 && map[i][j] != 6) {
				cctv_list.push_back({map[i][j], 0, i, j}); // {CCTV종류, 방향, y좌표, x좌표}
			}
		}
	}
}

bool isRanged(int y, int x) {
	if (y >= N || x >= M || y < 0 || x < 0) return false;
	return true;
}

void fill_line(vector<vector<int>> &tmp, int sy, int sx, int direct) {
	while (isRanged(sy, sx) && map[sy][sx] != 6) {
		tmp[sy][sx] = 9;
		sy += DIRECTION[direct].y;
		sx += DIRECTION[direct].x;
	}
}

void fill_map(vector<vector<int>> &tmp, cctv c) {
	if (c.type == 1) {
		fill_line(tmp, c.y, c.x, c.direct);
	}
	else if (c.type == 2) {
		fill_line(tmp, c.y, c.x, c.direct);
		fill_line(tmp, c.y, c.x, c.direct + 2);
	}
	else if (c.type == 3) {
		fill_line(tmp, c.y, c.x, c.direct);
		fill_line(tmp, c.y, c.x, (c.direct + 1) % 4);
	}
	else if (c.type == 4) {
		fill_line(tmp, c.y, c.x, c.direct);
		fill_line(tmp, c.y, c.x, (c.direct == 0 ? 3 : c.direct - 1));
		fill_line(tmp, c.y, c.x, (c.direct + 1) % 4);
	}
	else {
		for (int direct = 0; direct < 4; ++direct)
			fill_line(tmp, c.y, c.x, direct);
	}
}

int count_area(vector<vector<int>> &tmp) {
	int ret = 0;
	for (int i = 0; i < N; ++i) {
		for (int j = 0; j < M; ++j) {
			if (tmp[i][j] == 0) ++ret;
		}
	}
	return ret;
}

vector<vector<int>> copy_map(vector<vector<int>> &from, vector<vector<int>> &to) {
	for (int i = 0; i < N; ++i) {
		for (int j = 0; j < M; ++j) {
			to[i][j] = from[i][j];
		}
	}
	return to;
}

int nCm(int cnt, int prev) {
	if (cnt == cctv_list.size()) {
		tmp = copy_map(map, tmp);
		for (int i = 0; i < selected.size(); ++i)
			fill_map(tmp, selected[i]);
		int ret = count_area(tmp);
		return ret;
	}
	int ret = INT_MAX;
	for (int next = prev + 1; next < cctv_list.size(); ++next) {
		for (int direct = 0; direct < num_direct[cctv_list[next].type]; ++direct) {
			selected.push_back({cctv_list[next].type, direct, cctv_list[next].y, cctv_list[next].x});// 현재 cctv type, direct를 저장한다.
			ret = min (ret, nCm(cnt + 1, next));
			selected.pop_back();// 칠한 map을 다시 되돌린다.
		}
	}
	return ret;
}

int main() {
	ios_base::sync_with_stdio(false); cin.tie(NULL); cout.tie(NULL);
	input();
	cout << nCm(0, -1);
	return 0;
}

 

개발 환경은 VS 2019입니다.

질문, 지적, 덧글 환영합니다!!

'알고리즘 > 백준' 카테고리의 다른 글

[C++] 백준 14891 톱니바퀴  (0) 2020.05.25
[C++} 백준 14890 경사로  (0) 2020.05.25
[C++] 백준 14500 테트로미노  (0) 2020.05.20
[C++] 백준 2206 벽 부수고 이동하기  (0) 2020.05.19
[C++] 백준 1697 숨바꼭질  (0) 2020.05.19

+ Recent posts