[아이디어]

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

 

지시문 #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

+ Recent posts