[아이디어]

중간에 삽질하느라 디버깅하는데 오래 걸리기도 하고,

 

지시문 #define에 대해서...

[들어가기 전] #define R_MAX 20 + 1 ~~~ int wall[R_MAX * 2][R_MAX * 2] 위 코드에서 R_MAX * 2는 어떤 정수일까요? 1. 42 2. 22 정답을 아시는 분은 뒤로 가셔도 좋습니다! ㅎㅎ 정답은 2번 입니다! 왜 이런 결과가 나

kansstory.tistory.com

 

복잡했지만 단순 구현 문제였습니다.

 

단지 BFS를 이용해

인접한 네 방향을 주로 탐색했다면,

한 쪽 방향으로 퍼지게끔

구현에 변형이 필요했습니다.

 

또한 각 칸 사이엔 벽이 존재할 수 있는데

벽의 정보는

map의 크기를 2배로 늘려

사이사이 벽의 위치에 대한 좌표를 구할 수 있게끔 설계했습니다.

 

 

[요약]

1. 온풍기에서 바람이 퍼지듯이 불고

2. 온도를 조절하며

3. 최외곽의 온도가 1이상이라면 1씩 감소시키고

4. 초콜릿을 드시면

됩니다.

 

 

[방법]

바람이 한 방향으로 퍼지면서 나아간다.

이 부분은 BFS탐색 기법을 활용해 구현했습니다.

저는 방향벡터로

int dy[5] = { 0,0,0,-1,1 }; // 동 서 북 남
int dx[5] = { 0,1,-1,0,0 };

를 사용했습니다.

 

위 그림처럼 온풍기의 바람 방향이 동쪽(1)일 때,

방향벡터 배열 1~4까지 탐색하면서

 

동쪽(1)일 때,

동쪽(1) 방향으로 벽의 유무를 확인한 뒤

벽이 없다면 이동하고

 

서쪽(2)은 지나치고

 

북쪽(3)이나 남쪽(4)일 때,

위, 아래에 벽의 유무를 확인한 뒤

벽이 없다면 이동하고

동쪽(1)의 벽의 유무를 확인한 뒤

벽이 없다면 이동하도록

구현했습니다.

 

 

벽 위치 저장

map배열 좌표에 근거해

(r, c, d) = {(5, 4, 0), (4, 4, 1), (5, 6, 0)}

입력을 받는다면,

벽의 위치는 wall배열에 따르면

(r, c) = {(8, 7), (7,8), (8,11)}

에 저장되어 있게 됩니다.

 

따라서,

map배열에서 r좌표가 x이라면

wall배열에서 r좌표는 (x * 2) -1 임이 성립됩니다.

map배열 좌표에서 r좌표가 x, x+1인

사이의 벽은

wall배열에서 (x * 2)임이 성립됩니다.

 

이를 가정하고 wall 배열을 생성시, 

map 배열 크기의 2배만큼 키워

벽의 좌표를 저장했습니다.

 

[설명]

blow_from_heater(온풍기 인덱스)

주사위를 이동시켰을 때,

주사위의 좌표, 방향, 전개도, 획득한 점수 등을

업데이트해주는 함수

void blow_from_heater(int num) {
	/*
	온풍기에서 바람이 분다.
	*/
	queue<pair<int, int>> q; // 탐색할 좌표를 순서대로 집어 넣음
	int heater_map[R_MAX][C_MAX] = { 0, }; // 바람이 불면서 몇도의 온도가 저장되는지 기록
	int used[R_MAX][C_MAX] = { 0, }; // 이미 바람이 지나갔는지 확인
	NODE* now_heater = &heater[num]; // heater배열에서 현재 온풍기에 대한 정보를 저장해둠

	// 온풍기에서 바람이 불기 시작하는 위치
	int sy = now_heater->y + dy[now_heater->d];
	int sx = now_heater->x + dx[now_heater->d];

	heater_map[sy][sx] = 5;
	used[sy][sx] = 1;
	q.push(make_pair(sy, sx));

    while (!q.empty()) {
        pair<int, int> now = q.front();
        q.pop();

        FOR(i, 1, 5) {
            if (now_heater->d == 1 && i == 2) continue; // 온풍기 방향이 동쪽이면 서쪽은 탐색안함
            else if (now_heater->d == 2 && i == 1) continue; // 온풍기 방향이 서쪽이면 동쪽은 탐색안함
            else if (now_heater->d == 3 && i == 4) continue; // 온풍기 방향이 북쪽이면 남쪽은 탐색안함
            else if (now_heater->d == 4 && i == 3) continue; // 온풍기 방향이 남쪽이면 북쪽은 탐색안함
            
            // 현재 위치에 대한 벽의 유무를 파악
            int ty = (now.first * 2) - 1 + dy[i];
            int tx = (now.second  * 2) - 1 + dx[i];
            if (ty < 1 || ty >((R * 2) - 1) || tx < 1 || tx >((C * 2) - 1)) continue; // 범위를 벗어난 벽을 탐색하지 않는지 확인
            if (wall[ty][tx] == 1) continue; // 벽에 막혔다면 탐색안함

            // 다음 바람이 불 다음 위치 업데이트
            int ny = now.first + dy[i];
            int nx = now.second + dx[i];
            if (i != now_heater->d) {
            	/*
                    북쪽이나 남쪽이라면 바람이 불던 방향(동 or 서)으로 한번더 꺽어줘야함
                    동쪽이나 서쪽이라면 바람이 불던 방향(북 or 남)으로 한번더 꺽어줘야함
                */
                    
                // 현재 위치에 대한 벽의 유무파악
                ty = (ny * 2) - 1 + dy[now_heater->d];
                tx = (nx * 2) - 1 + dx[now_heater->d];
                if (ty < 1 || ty >((R * 2) - 1) || tx < 1 || tx >((C * 2) - 1)) continue; // 범위를 벗어난 벽을 탐색하지 않는지 확인
                if (wall[ty][tx] == 1) continue; // 벽에 막혔다면

                // 다음 바람이 불 다음 위치 업데이트
                ny = ny + dy[now_heater->d];
                nx = nx + dx[now_heater->d];
            }

            if (ny < 1 || ny > R || nx < 1 || nx > C) continue; //맵 밖으로 바람이 불진 않는지 확인
            if (heater_map[ny][nx] != 0) continue; // 이미 바람이 불었던 위치가 아닌지 확인
            if (used[ny][nx] != 0) continue; // 이미 바람이 불었던 위치가 아닌지 확인

            if ((heater_map[now.first][now.second] - 1) < 1) continue; // 온도가 0이하라면 더이상 바람이 닿지 않음
            heater_map[ny][nx] = heater_map[now.first][now.second] - 1; // 다음 바람이 불 위치의 온도를 저장
            used[ny][nx] = 1; // 바람이 지나간 자리임을 표시
            q.push(make_pair(ny, nx));
        }
    }

	add_heaterMap_to_temper(heater_map);
}

 

add_heaterMap_to_temper(int heater_map[][C_MAX])

전체 온도를 기록해둔 temper맵에

온풍기에서 바람이 나온 상태의 온도를 기록해둔 heater_map맵의 값만큼

더해줌

void add_heaterMap_to_temper(int heater_map[][C_MAX]) {
	FOR(y, 1, R + 1) {
		FOR(x, 1, C + 1) {
			temper[y][x] += heater_map[y][x];
		}
	}
}

 

control_temper()

주위 온도를 살펴보며 온도를 조절해줌

void control_temper() {
	/*
	온도 조절
	*/
	int spread_temper[R_MAX][C_MAX] = { 0, }; // 온도 조절하기 위해 주변으로 퍼진 온도 값 저장
	int flow_out_temper[R_MAX][C_MAX] = { 0, }; // 온도 조절하기 위해 주변으로 퍼지기 위해 빠져나간 온도 값 저장

	FOR(y, 1, R + 1) {
		FOR(x, 1, C + 1) {
			spread_temper[y][x] = 0;
			flow_out_temper[y][x] = 0;
		}
	}

	FOR(y, 1, (R + 1)) {
		FOR(x, 1, (C + 1)) {
			if (temper[y][x] != 0) {
				FOR(i, 1, 5) {
                	// 벽의 유무 확인
					int ty = (y * 2) - 1 + dy[i];
					int tx = (x * 2) - 1 + dx[i];
					if (ty < 1 || ty >((R * 2) - 1) || tx < 1 || tx >((C * 2) - 1)) continue;
					if (wall[ty][tx] == 1) continue;

					// 온도가 빠져나갈 좌표 확인
					int ny = y + dy[i];
					int nx = x + dx[i];
					if (ny<1 || ny > R || nx < 1 || nx > C) continue;

					if (temper[y][x] > temper[ny][nx]) {
						int L = (temper[y][x] - temper[ny][nx]) >> 2; // (두칸의 온도차 / 4)

						spread_temper[ny][nx] += L;
						flow_out_temper[y][x] -= L;
					}
				}
				flow_out_temper[y][x] += temper[y][x];
			}
		}
	}

	FOR(y, 1, (R + 1)) {
		FOR(x, 1, (C + 1)) {
			temper[y][x] = flow_out_temper[y][x] + spread_temper[y][x];
		}
	}
}

 

decrease_outside_temper()

최외곽 좌표에 온도가 1이상이면 1씩 감소

void decrease_outside_temper() {
	/*
	가장 바깥쪽 칸의 온도 1씩 감소
	*/
	FOR(x, 1, C) {
		if (temper[1][x] >= 1) {
			temper[1][x] -= 1;
		}
	}
	FOR(x, 2, (C + 1)) {
		if (temper[R][x] >= 1) {
			temper[R][x] -= 1;
		}
	}
	FOR(y, 2, (R + 1)) {
		if (temper[y][1] >= 1) {
			temper[y][1] -= 1;
		}
	}
	FOR(y, 1, R) {
		if (temper[y][C] >= 1) {
			temper[y][C] -= 1;
		}
	}
}

 

eat_chocolate()

초콜릿 먹기

void eat_chocolate() {
	/* 초콜릿 먹기 */
	++chocolate;
}

 

isReachTargetTemper()

조사할 위치가 목표 온도에 도달했는지

bool isReachTargetTemper() {
	/* 목표 온도에 도달 했는지 */
	FOR(i, 0, t_cnt) {
		if (temper[target[i].y][target[i].x] < K) {
			return false;
		}
	}
	return true;
}

 

 

 

[코드]

#include <iostream>
#include <vector>
#include <queue>
#include <cstring>
using namespace std;

#define R_MAX (20 + 1)
#define C_MAX (20 + 1)
#define WALL_MAX 21
#define FOR(i,s,e) for(int i=s; i<e; ++i)

int R, C, K;
int map[R_MAX][C_MAX];
struct NODE {
	int y;
	int x;
	int d;
};
NODE heater[410];
NODE target[410];
int dy[5] = { 0,0,0,-1,1 }; // 동 서 북 남
int dx[5] = { 0,1,-1,0,0 };
int W;
int wall[R_MAX * 2][C_MAX * 2];
int temper[R_MAX][C_MAX];
int h_cnt;
int t_cnt;
int chocolate;

void add_heaterMap_to_temper(int heater_map[][C_MAX]) {
	FOR(y, 1, R + 1) {
		FOR(x, 1, C + 1) {
			temper[y][x] += heater_map[y][x];
		}
	}
}

void blow_from_heater(int num) {
	/*
	온풍기에서 바람이 분다.
	*/
	queue<pair<int, int>> q;
	int heater_map[R_MAX][C_MAX] = { 0, };
	int used[R_MAX][C_MAX] = { 0, };
	NODE* now_heater = &heater[num];

	int sy = now_heater->y + dy[now_heater->d];
	int sx = now_heater->x + dx[now_heater->d];

	heater_map[sy][sx] = 5;
	used[sy][sx] = 1;
	q.push(make_pair(sy, sx));

	while (!q.empty()) {
		pair<int, int> now = q.front();
		q.pop();

		FOR(i, 1, 5) {
			if (now_heater->d == 1 && i == 2) continue;
			else if (now_heater->d == 2 && i == 1) continue;
			else if (now_heater->d == 3 && i == 4) continue;
			else if (now_heater->d == 4 && i == 3) continue;

			int ty = (now.first * 2) - 1 + dy[i];
			int tx = (now.second  * 2) - 1 + dx[i];
			if (ty < 1 || ty >((R * 2) - 1) || tx < 1 || tx >((C * 2) - 1)) continue;
			if (wall[ty][tx] == 1) continue; // 벽에 막혔다면

			int ny = now.first + dy[i];
			int nx = now.second + dx[i];
			if (i != now_heater->d) {
				ty = (ny * 2) - 1 + dy[now_heater->d];
				tx = (nx * 2) - 1 + dx[now_heater->d];
				if (ty < 1 || ty >((R * 2) - 1) || tx < 1 || tx >((C * 2) - 1)) continue;
				if (wall[ty][tx] == 1) continue; // 벽에 막혔다면

				ny = ny + dy[now_heater->d];
				nx = nx + dx[now_heater->d];
			}

			if (ny < 1 || ny > R || nx < 1 || nx > C) continue;
			if (heater_map[ny][nx] != 0) continue;
			if (used[ny][nx] != 0) continue;

			if ((heater_map[now.first][now.second] - 1) < 1) continue;
			heater_map[ny][nx] = heater_map[now.first][now.second] - 1;
			used[ny][nx] = 1;
			q.push(make_pair(ny, nx));
		}
	}

	add_heaterMap_to_temper(heater_map);
}

void control_temper() {
	/*
	온도 조절
	*/
	int spread_temper[R_MAX][C_MAX] = { 0, };
	int flow_out_temper[R_MAX][C_MAX] = { 0, };

	FOR(y, 1, R + 1) {
		FOR(x, 1, C + 1) {
			spread_temper[y][x] = 0;
			flow_out_temper[y][x] = 0;
		}
	}

	FOR(y, 1, (R + 1)) {
		FOR(x, 1, (C + 1)) {
			if (temper[y][x] != 0) {
				FOR(i, 1, 5) {
					int ty = (y * 2) - 1 + dy[i];
					int tx = (x * 2) - 1 + dx[i];
					if (ty < 1 || ty >((R * 2) - 1) || tx < 1 || tx >((C * 2) - 1)) continue;
					if (wall[ty][tx] == 1) continue;

					int ny = y + dy[i];
					int nx = x + dx[i];
					if (ny<1 || ny > R || nx < 1 || nx > C) continue;

					if (temper[y][x] > temper[ny][nx]) {
						int L = (temper[y][x] - temper[ny][nx]) >> 2; // (두칸의 온도차 / 4)

						spread_temper[ny][nx] += L;
						flow_out_temper[y][x] -= L;
					}
				}
				flow_out_temper[y][x] += temper[y][x];
			}
		}
	}

	FOR(y, 1, (R + 1)) {
		FOR(x, 1, (C + 1)) {
			temper[y][x] = flow_out_temper[y][x] + spread_temper[y][x];
		}
	}
}

void decrease_outside_temper() {
	/*
	가장 바깥쪽 칸의 온도 1씩 감소
	*/
	FOR(x, 1, C) {
		if (temper[1][x] >= 1) {
			temper[1][x] -= 1;
		}
	}
	FOR(x, 2, (C + 1)) {
		if (temper[R][x] >= 1) {
			temper[R][x] -= 1;
		}
	}
	FOR(y, 2, (R + 1)) {
		if (temper[y][1] >= 1) {
			temper[y][1] -= 1;
		}
	}
	FOR(y, 1, R) {
		if (temper[y][C] >= 1) {
			temper[y][C] -= 1;
		}
	}
}

void eat_chocolate() {
	/* 초콜릿 먹기 */
	++chocolate;
}

bool isReachTargetTemper() {
	/* 목표 온도에 도달 했는지 */
	FOR(i, 0, t_cnt) {
		if (temper[target[i].y][target[i].x] < K) {
			return false;
		}
	}
	return true;
}

void sol() {
	while (isReachTargetTemper() == false) {
		FOR(i, 0, h_cnt) {
			blow_from_heater(i);
		}
		control_temper();
		decrease_outside_temper();
		eat_chocolate();
		if (chocolate > 100) {
			chocolate = 101;
			break;
		}
	}
	cout << chocolate;
}

int main() {
	ios::sync_with_stdio(false);
	cin.tie(NULL);
	cout.tie(NULL);

	freopen("input.txt", "r", stdin);
	cin >> R >> C >> K;
	FOR(y, 1, (R + 1)) {
		FOR(x, 1, (C + 1)) {
			cin >> map[y][x];
			if (map[y][x] == 5) {
				target[t_cnt].y = y;
				target[t_cnt++].x = x;
			}
			else if (map[y][x] != 0) {
				heater[h_cnt].y = y;
				heater[h_cnt].x = x;
				heater[h_cnt++].d = map[y][x];
			}
		}
	}
	cin >> W;
	FOR(i, 0, W) {
		int r = 0, c = 0, d = 0;
		cin >> r >> c >> d;
		int y = (r * 2) - 1;
		int x = (c * 2) - 1;
		if (d == 0) {
			wall[y - 1][x] = 1;
		}
		else if (d == 1) {
			wall[y][x + 1] = 1;
		}
	}

	sol();

	return 0;
}

 

[문제 출처]

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

반응형

'Problem Solving > 백준' 카테고리의 다른 글

[백준_23288] 주사위 굴리기 2 (C++)  (4) 2024.01.29

[아이디어]

주사위 굴릴 때마다 변하는 전개도는 단순 구현

BFS 탐색기법을 활용해

풀었습니다.

 

 

[요약]

주사위가 특정 조건에 따라 이동한다.

이동 후 도착한 칸에 대한 점수를 획득한다.

다음 이동 방향을 결정한다.

 

 

[방법]

주사위를 굴릴때마다 주사위 전개도의 눈의 위치가 업데이트되도록 구현했습니다.

예를 들어,

주사위를 동쪽으로 굴렸을 때

 

주사위 눈금 2의 좌표(row, col)를 (0, 1)라고 한다면

(3, 1) 과 (1, 3)의 좌표의 값이 변화가 있다면 같도록 갱신해주어야 합니다.

 

위 그림에선 동쪽으로 주사위를 굴렸기에

1행 = {4, 1, 3, 6}이 오른쪽으로 한 칸씩 밀려

{6, 4, 1, 3}으로 변하게 됩니다.

이후 좌표 (3, 1)의 값을 (1, 3)의 값과 같도록 업데이트해주었습니다.

(((문제에선 좌표 (1, 1) 주사위의 '윗면'에 해당합니다.)))

 

만약, 남쪽이나 북쪽으로 주사위가 이동한다면

역시 아래쪽이나 위로 한 칸씩 1열에 해당하는 원소를 밀고,

(1, 3)의 값을 (3, 1)의 값과 같도록 업데이티 해주면 됩니다.

 

저의 경우에는 vector라이브러리를 사용한다면

일반 배열을 사용할 때보다 코드 작성이 간단하기에

vector라이브러리 채택해 사용했습니다.

void roll_dice() {
	if (dice.dir == 0 || dice.dir == 2) {
		if (dice.dir == 0) { // 주사위를 동쪽으로 굴린다.
			vector<int> tmp = dice.dice_num[1];
			tmp = { tmp[3], tmp[0], tmp[1], tmp[2] };
			dice.dice_num[1] = tmp;
		}
		else if (dice.dir == 2) { // 주사위를 서쪽으로 굴린다.
			vector<int> tmp = dice.dice_num[1];
			tmp = { tmp[1], tmp[2], tmp[3], tmp[0] };
			dice.dice_num[1] = tmp;
		}
		dice.dice_num[3][1] = dice.dice_num[1][3]; // 주사위 전개도상 '아랫면'에 해당하는 부분 업데이트
	}
	else if (dice.dir == 1 || dice.dir == 3) {
		if (dice.dir == 1) { // 주사위를 남쪽으로 굴린다.
			vector<int> tmp = { dice.dice_num[0][1], dice.dice_num[1][1], dice.dice_num[2][1], dice.dice_num[3][1] };
			tmp = { tmp[3], tmp[0], tmp[1], tmp[2] };
			dice.dice_num[0][1] = tmp[0];
			dice.dice_num[1][1] = tmp[1];
			dice.dice_num[2][1] = tmp[2];
			dice.dice_num[3][1] = tmp[3];
		}
		else if (dice.dir == 3) { // 주사위를 북쪽으로 굴린다.
			vector<int> tmp = { dice.dice_num[0][1], dice.dice_num[1][1], dice.dice_num[2][1], dice.dice_num[3][1] };
			tmp = { tmp[1], tmp[2], tmp[3], tmp[0] };
			dice.dice_num[0][1] = tmp[0];
			dice.dice_num[1][1] = tmp[1];
			dice.dice_num[2][1] = tmp[2];
			dice.dice_num[3][1] = tmp[3];
		}
		dice.dice_num[1][3] = dice.dice_num[3][1]; // 주사위 전개도상 '아랫면'에 해당하는 부분 업데이트
	}
}

 

나머지는 주사위 이동 후

BFS탐색 기법을 활용해

점수 계산과 주사위의 다음 방향 설정을 해주면 끝입니다.

 

 

[설명]

move_dice()

주사위를 이동시켰을 때,

주사위의 좌표, 방향, 전개도, 획득한 점수 등을

업데이트해주는 함수

void move_dice() {
	/*
	주사위를 이동시켰을 때,
	주사위의 좌표, 방향, 전개도, 획득한 점수 등을
	업데이트 해주는 함수
	*/
	if (isMove() == false) {
		// 주사위가 이동할 수 없는 방향이라면 반대 방향으로 바꿔줌
		if (dice.dir == 0) dice.dir = 2;
		else if (dice.dir == 2) dice.dir = 0;
		else if (dice.dir == 1) dice.dir = 3;
		else if (dice.dir == 3) dice.dir = 1;
	}

	// 주사위가 이동한 좌표 업데이트
	dice.y = dice.y + dy[dice.dir];
	dice.x = dice.x + dx[dice.dir];

	// 주사위를 굴렸을 때 바뀐 전개도 업데이트
	roll_dice();

	// 주사위가 획득한 점수 계산
	int s = bfs();
	score += (map[dice.y][dice.x] * s);

	// A는 주사위의 '아랫면'의 눈의 값을 저장
	// B는 지도상에서 주사위가 이동한 위치의 정수 값
	int A = dice.dice_num[3][1];
	int B = map[dice.y][dice.x];

	if (A > B) { // A > B 라면 시계 방향으로 90도 회전
		dice.dir += 1;
		if (dice.dir >= 4) dice.dir = 0;
	}
	else if (A < B) { // A < B 라면 반시계 방향으로 90도 회전
		dice.dir -= 1;
		if (dice.dir < 0) dice.dir = 3;
	}
}

 

isMove()

주사위가 이미 지정된 방향으로

이동이 가능한지 여부를 확인하는 함수

bool isMove() {
	/*
	주사위가 설정한 방향으로 이동이 가능한지 확인

	가능하다면 true
	불가능하다면 false
	*/
	int ny = dice.y + dy[dice.dir];
	int nx = dice.x + dx[dice.dir];

	if (ny < 1 || ny > N || nx < 1 || nx > M) return false;

	return true;
}

 

roll_dice()

주사위를 굴리면서 변화하는 전개도를 업데이트 해주는 함수

void roll_dice() {
	if (dice.dir == 0 || dice.dir == 2) {
		if (dice.dir == 0) { // 주사위를 동쪽으로 굴린다.
			vector<int> tmp = dice.dice_num[1];
			tmp = { tmp[3], tmp[0], tmp[1], tmp[2] };
			dice.dice_num[1] = tmp;
		}
		else if (dice.dir == 2) { // 주사위를 서쪽으로 굴린다.
			vector<int> tmp = dice.dice_num[1];
			tmp = { tmp[1], tmp[2], tmp[3], tmp[0] };
			dice.dice_num[1] = tmp;
		}
		dice.dice_num[3][1] = dice.dice_num[1][3]; // 주사위 전개도상 '아랫면'에 해당하는 부분 업데이트
	}
	else if (dice.dir == 1 || dice.dir == 3) {
		if (dice.dir == 1) { // 주사위를 남쪽으로 굴린다.
			vector<int> tmp = { dice.dice_num[0][1], dice.dice_num[1][1], dice.dice_num[2][1], dice.dice_num[3][1] };
			tmp = { tmp[3], tmp[0], tmp[1], tmp[2] };
			dice.dice_num[0][1] = tmp[0];
			dice.dice_num[1][1] = tmp[1];
			dice.dice_num[2][1] = tmp[2];
			dice.dice_num[3][1] = tmp[3];
		}
		else if (dice.dir == 3) { // 주사위를 북쪽으로 굴린다.
			vector<int> tmp = { dice.dice_num[0][1], dice.dice_num[1][1], dice.dice_num[2][1], dice.dice_num[3][1] };
			tmp = { tmp[1], tmp[2], tmp[3], tmp[0] };
			dice.dice_num[0][1] = tmp[0];
			dice.dice_num[1][1] = tmp[1];
			dice.dice_num[2][1] = tmp[2];
			dice.dice_num[3][1] = tmp[3];
		}
		dice.dice_num[1][3] = dice.dice_num[3][1]; // 주사위 전개도상 '아랫면'에 해당하는 부분 업데이트
	}
}

 

bfs()

지도상의 주사위가 이동한 위치의 정수 값과

주사위 위치의 동서남북으로 인접한 위치의 정수 값이

일치하는 좌표의 개수를 반환해 주는 함수

int bfs() {
	/*
	지도상에서 주사위가 이동한 위치의 정수 값과 
	동서남북으로 인접한 좌표에 일치하는 값이 있는지 탐색

	val은 지도상에서 주사위가 이동한 위치의 정수 값 저장
	cnt는 동일한 값이 인접한 좌표에 몇개가 존재하는지 찾음
	*/
	queue<pair<int, int>> q;
	int used[N_MAX][M_MAX] = { 0, };

	int val = map[dice.y][dice.x];
	int cnt = 1;

	q.push(make_pair(dice.y, dice.x));
	used[dice.y][dice.x] = 1;

	while (!q.empty()) {
		pair<int, int> now = q.front();
		q.pop();

		FOR(i, 0, 4) {
			int ny = now.first + dy[i];
			int nx = now.second + dx[i];

			if (ny<1 || ny>N || nx<1 || nx>M) continue;
			if (used[ny][nx] == 1) continue;
			if (map[ny][nx] != val)continue;

			used[ny][nx] = 1;
			++cnt;
			q.push(make_pair(ny, nx));
		}
	}

	return cnt;
}

 

 

 

[코드]

#include <iostream>
#include <vector>
#include <queue>
using namespace std;

#define N_MAX 20 + 1
#define M_MAX 20 + 1
#define FOR(i,s,e) for(int i=s; i<e; ++i)

struct DICE { // 주사위의 정보를 담은 구조체
	int y;
	int x;
	int dir;
	vector<vector<int>> dice_num{ // 주사위 전개도
		{0, 2, 0, 0},
		{ 4,1,3,6 },
		{ 0,5,0,0 },
		{ 0,6,0,0 }
	};
};
int N, M, K;
int map[N_MAX][M_MAX];
int dy[4] = { 0,1,0,-1 }; // 동 남 북 서
int dx[4] = { 1,0,-1,0 };
DICE dice;
int score;

void init_dice() {
	dice.y = 1;
	dice.x = 1;
	dice.dir = 0; // 시작은 동쪽
}

bool isMove() {
	/*
	주사위가 설정한 방향으로 이동이 가능한지 확인

	가능하다면 true
	불가능하다면 false
	*/
	int ny = dice.y + dy[dice.dir];
	int nx = dice.x + dx[dice.dir];

	if (ny < 1 || ny > N || nx < 1 || nx > M) return false;

	return true;
}

void roll_dice() {
	if (dice.dir == 0 || dice.dir == 2) {
		if (dice.dir == 0) { // 주사위를 동쪽으로 굴린다.
			vector<int> tmp = dice.dice_num[1];
			tmp = { tmp[3], tmp[0], tmp[1], tmp[2] };
			dice.dice_num[1] = tmp;
		}
		else if (dice.dir == 2) { // 주사위를 서쪽으로 굴린다.
			vector<int> tmp = dice.dice_num[1];
			tmp = { tmp[1], tmp[2], tmp[3], tmp[0] };
			dice.dice_num[1] = tmp;
		}
		dice.dice_num[3][1] = dice.dice_num[1][3]; // 주사위 전개도상 '아랫면'에 해당하는 부분 업데이트
	}
	else if (dice.dir == 1 || dice.dir == 3) {
		if (dice.dir == 1) { // 주사위를 남쪽으로 굴린다.
			vector<int> tmp = { dice.dice_num[0][1], dice.dice_num[1][1], dice.dice_num[2][1], dice.dice_num[3][1] };
			tmp = { tmp[3], tmp[0], tmp[1], tmp[2] };
			dice.dice_num[0][1] = tmp[0];
			dice.dice_num[1][1] = tmp[1];
			dice.dice_num[2][1] = tmp[2];
			dice.dice_num[3][1] = tmp[3];
		}
		else if (dice.dir == 3) { // 주사위를 북쪽으로 굴린다.
			vector<int> tmp = { dice.dice_num[0][1], dice.dice_num[1][1], dice.dice_num[2][1], dice.dice_num[3][1] };
			tmp = { tmp[1], tmp[2], tmp[3], tmp[0] };
			dice.dice_num[0][1] = tmp[0];
			dice.dice_num[1][1] = tmp[1];
			dice.dice_num[2][1] = tmp[2];
			dice.dice_num[3][1] = tmp[3];
		}
		dice.dice_num[1][3] = dice.dice_num[3][1]; // 주사위 전개도상 '아랫면'에 해당하는 부분 업데이트
	}
}

int bfs() {
	/*
	지도상에서 주사위가 이동한 위치의 정수 값과 
	동서남북으로 인접한 좌표에 일치하는 값이 있는지 탐색

	val은 지도상에서 주사위가 이동한 위치의 정수 값 저장
	cnt는 동일한 값이 인접한 좌표에 몇개가 존재하는지 찾음
	*/
	queue<pair<int, int>> q;
	int used[N_MAX][M_MAX] = { 0, };

	int val = map[dice.y][dice.x];
	int cnt = 1;

	q.push(make_pair(dice.y, dice.x));
	used[dice.y][dice.x] = 1;

	while (!q.empty()) {
		pair<int, int> now = q.front();
		q.pop();

		FOR(i, 0, 4) {
			int ny = now.first + dy[i];
			int nx = now.second + dx[i];

			if (ny<1 || ny>N || nx<1 || nx>M) continue;
			if (used[ny][nx] == 1) continue;
			if (map[ny][nx] != val)continue;

			used[ny][nx] = 1;
			++cnt;
			q.push(make_pair(ny, nx));
		}
	}

	return cnt;
}

void move_dice() {
	/*
	주사위를 이동시켰을 때,
	주사위의 좌표, 방향, 전개도, 획득한 점수 등을
	업데이트 해주는 함수
	*/
	if (isMove() == false) {
		// 주사위가 이동할 수 없는 방향이라면 반대 방향으로 바꿔줌
		if (dice.dir == 0) dice.dir = 2;
		else if (dice.dir == 2) dice.dir = 0;
		else if (dice.dir == 1) dice.dir = 3;
		else if (dice.dir == 3) dice.dir = 1;
	}

	// 주사위가 이동한 좌표 업데이트
	dice.y = dice.y + dy[dice.dir];
	dice.x = dice.x + dx[dice.dir];

	// 주사위를 굴렸을 때 바뀐 전개도 업데이트
	roll_dice();

	// 주사위가 획득한 점수 계산
	int s = bfs();
	score += (map[dice.y][dice.x] * s);

	// A는 주사위의 '아랫면'의 눈의 값을 저장
	// B는 지도상에서 주사위가 이동한 위치의 정수 값
	int A = dice.dice_num[3][1];
	int B = map[dice.y][dice.x];

	if (A > B) { // A > B 라면 시계 방향으로 90도 회전
		dice.dir += 1;
		if (dice.dir >= 4) dice.dir = 0;
	}
	else if (A < B) { // A < B 라면 반시계 방향으로 90도 회전
		dice.dir -= 1;
		if (dice.dir < 0) dice.dir = 3;
	}
}

void sol() {
	init_dice();
	FOR(i, 0, K) {
		move_dice();
	}
	cout << score;
}

int main() {
	ios::sync_with_stdio(false);
	cin.tie(NULL);
	cout.tie(NULL);

	freopen("input.txt", "r", stdin);
	cin >> N >> M >> K;
	FOR(y, 1, (N + 1)) {
		FOR(x, 1, (M + 1)) {
			cin >> map[y][x];
		}
	}
	sol();

	return 0;
}

 

[문제 출처]

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

반응형

'Problem Solving > 백준' 카테고리의 다른 글

[백준_23289] 온풍기 안녕! (C++)  (2) 2024.02.03

+ Recent posts