1. 문제 설명
- 문제 링크
- N×N인 격자
- 절차
- 모든 구름이 di 방향으로 si칸 이동한다.
- 각 구름에서 비가 내려 구름이 있는 칸의 바구니에 저장된 물의 양이 1 증가한다.
- 구름이 모두 사라진다.
- 2에서 물이 증가한 칸 (r, c)에 물복사버그 마법을 시전한다. 물복사버그 마법을 사용하면, 대각선 방향으로 거리가 1인 칸에 물이 있는 바구니의 수만큼 (r, c)에 있는 바구니의 물이 양이 증가한다.
- 이때는 이동과 다르게 경계를 넘어가는 칸은 대각선 방향으로 거리가 1인 칸이 아니다.
- 예를 들어, (N, 2)에서 인접한 대각선 칸은 (N-1, 1), (N-1, 3)이고, (N, N)에서 인접한 대각선 칸은 (N-1, N-1)뿐이다.
- 바구니에 저장된 물의 양이 2 이상인 모든 칸에 구름이 생기고, 물의 양이 2 줄어든다. 이때 구름이 생기는 칸은 3에서 구름이 사라진 칸이 아니어야 한다.
첫째 줄에 N, M이 주어진다.
둘째 줄부터 N개의 줄에는 N개의 정수가 주어진다. r번째 행의 c번째 정수는 A[r][c]를 의미한다.
다음 M개의 줄에는 이동의 정보 di, si가 순서대로 한 줄에 하나씩 주어진다.
2. 풀이
- 시물레이션 문제
- 구름 이동
- 정해진 방향으로 정해진 칸수 만큼 이동
- 쓸때 없는 반복을 줄이기 위해 나머지 연산 활용
- commands[idx].second % N;
- 구름 있는 칸 물량 증가
- commands[idx].second % N;
- 사라질 구름 표시
- 물복사 마법 시전
- 경계를 넘어가지 않고, 대각선 방향으로 거리가 1인 칸만 증가
- 구름 없애기
- 구름 생성
- 구름이 없어진 칸 제외 (2에서 표시한 배열 사용)
- 물양이 2 이상인 곳에 구름 생성
- 1-5 반복
- 명령 종료 후 물의양 다 더하기
3. 코드
- c++
#include <iostream>
#include <vector>
#include <cstring>
using namespace std;
// 맵크기
int N;
// 움직임 횟수
int M;
// 8방위 - 구름이동
int dx[9] = { 0, 0, -1, -1, -1, 0, 1, 1, 1 };
int dy[9] = { 0, -1, -1, 0, 1, 1, 1, 0, -1 };
// 대각선 4방위 - 물의 양 증가(주변 바구니 탐색)
int dx_diagonal[4] = { -1,1,-1,1 };
int dy_diagonal[4] = { -1,-1,1,1 };
// 물의 양이 증가한 바구니 리스트
vector<pair<int, int>> increased;
// 구름이 사라진 위치 표시
bool disappear[51][51];
// 각 바구니의 물의 양 정보
int map[51][51];
// 이동 명령
vector<pair<int, int>> commands;
// 구름 이동
vector<pair<int, int>> Move(vector<pair<int, int>>& clouds, int idx) {
int d = commands[idx].first;
int mv = commands[idx].second % N;
vector<pair<int, int>> new_clouds;
bool temp[51][51];
memset(temp, false, sizeof(temp));
for (int i = 0; i < clouds.size(); i++) {
// 구름 별 이 동후 좌표 설정
clouds[i].first = clouds[i].first + (dx[d] * mv);
if (clouds[i].first > N) clouds[i].first = clouds[i].first - N;
else if (clouds[i].first < 1) clouds[i].first = clouds[i].first + N;
clouds[i].second = clouds[i].second + (dy[d] * mv);
if (clouds[i].second > N) clouds[i].second = clouds[i].second - N;
else if (clouds[i].second < 1) clouds[i].second = clouds[i].second + N;
// 겹친 구름 하나로 만들기
if (!temp[clouds[i].first][clouds[i].second]) {
temp[clouds[i].first][clouds[i].second] = true;
new_clouds.push_back(make_pair(clouds[i].first, clouds[i].second));
}
}
// 이동된 구름들 좌표 반환
return new_clouds;
}
// 물복사 버그
void Bug(vector<pair<int, int>>& clouds) {
for (int i = 0; i < clouds.size(); i++) {
int x = clouds[i].first;
int y = clouds[i].second;
int iswater = 0;
for (int j = 0; j < 4; j++) {
int nx = x + dx_diagonal[j];
int ny = y + dy_diagonal[j];
if (nx < 1 || nx > N || ny < 1 || ny > N) continue;
if (map[nx][ny] > 0) iswater++;
}
map[x][y] += iswater;
}
}
void Print() {
for (int i = 1; i <= N; i++) {
for (int j = 1; j <= N; j++) {
cout << map[i][j] << "\t";
}
cout << endl;
}
cout << endl;
}
int main() {
// 맵크기 및 이동 횟수 설정
cin >> N >> M;
// 기본 물의 양 정보 입력
for (int i = 1; i <= N; i++) {
for (int j = 1; j <= N; j++) {
cin >> map[i][j];
}
}
// 이동 명령 입력
for (int i = 0; i < M; i++) {
int d, s;
cin >> d >> s;
commands.push_back(make_pair(d, s));
}
vector<pair<int, int>> clouds;
// 초기 구름
clouds.push_back(make_pair(N, 1));
clouds.push_back(make_pair(N, 2));
clouds.push_back(make_pair(N-1, 1));
clouds.push_back(make_pair(N-1, 2));
int idx = 0;
while (idx < M) {
// 1. 이동
clouds = Move(clouds, idx);
//cout << idx+1 << endl;
//cout << "구름 이동 후 " << clouds .size() << endl;
//Print();
memset(disappear, false, sizeof(disappear));
// 2. 구름 있는 칸 물량 증가
for (int i = 0; i < clouds.size(); i++) {
map[clouds[i].first][clouds[i].second]++;
disappear[clouds[i].first][clouds[i].second] = true;
}
//cout << "물량 증가후" << endl;
//Print();
// 3. 구름 사라짐 ( 나중에 처리)
// 4. 2에서 물이 증가한 칸에 물복사 버그
Bug(clouds);
//cout << "물 복사 버그 사용 후" << endl;
//Print();
// 5. 바구니 물의 양이 2이상인 곳에서 모두 그름 생성, 물의 양 2 감소
clouds.clear();
for (int i = 1; i <= N; i++) {
for (int j = 1; j <= N; j++) {
// 물의 양이 2인 곳에, 구름이 사라졌던 칸이 아니면
if (map[i][j] >= 2 && !disappear[i][j]) {
// 구름생성
clouds.push_back(make_pair(i, j));
// 물양 감소
map[i][j] -= 2;
}
}
}
//cout << "구름 생성 후" << endl;
//Print();
// 다음순서로 진행
idx++;
}
// 6. 끝난 후 물의 양 계산
int answer = 0;
for (int i = 1; i <= N; i++) {
for (int j = 1; j <= N; j++) {
answer += map[i][j];
}
}
cout << answer << endl;
return 0;
}
- python
- 코드 중복을 줄이기 위해서 check_boundary 함수 추가
- 알고리즘은 C++과 동일
# 구름 이동시켜 새로운 구름 리스트 반환
# 격자정보, 방향, 이동거리
def move_cloud(grid, clouds, dir, distance):
global N, dx, dy
def check_boundary(value):
# 오른쪽, 아래쪽으로 벗어나면 - N
if value >= N: return value - N
# 왼쪽이나, 위쪽으로 벗어나면 + N
elif value < 0: return value + N
# 격자 안이면 그냥 반환
return value
# 실제 이동거리(반복 제외)
actual_distance = distance % N
# 이동 후 구름 리스트
new_clouds = []
# 중복방지용 visited 배열
temp = [[False for _ in range(N)] for __ in range(N)]
for idx in range(len(clouds)):
# x 좌표 계산
clouds[idx][0] += (dx[dir] * actual_distance)
clouds[idx][0] = check_boundary(clouds[idx][0])
# y 좌표 계산
clouds[idx][1] += (dy[dir] * actual_distance)
clouds[idx][1] = check_boundary(clouds[idx][1])
# 같은 좌표로 이동하는 구름 중복 방지
# 방문한적 있는 좌표는 True 표시. 즉, 한번만 append
if not temp[clouds[idx][0]][clouds[idx][1]]:
temp[clouds[idx][0]][clouds[idx][1]] = True
new_clouds.append([clouds[idx][0], clouds[idx][1]])
return new_clouds
# 물복사 버그
def Bug(clouds, grid):
global N, dx_diagonal, dy_diagonal
# 구름이 있던곳 = 물의 양이 증가 한곳
for x, y in clouds:
# 증가할 물의양
iswater = 0
# 대각선 방향 검사
for dir in range(4):
nx = x + dx_diagonal[dir]
ny = y + dy_diagonal[dir]
if 0 <= nx < N and 0 <= ny < N:
# 물이 있는 바구니 수만큼 물의 양 증가
if grid[nx][ny] > 0:
# 물이 있으면 증가
iswater += 1
grid[x][y] += iswater
# main
# N, M 입력
N, M = map(int, input().split())
# 격자 정보 입력
grid = [list(map(int, input().split())) for _ in range(N)]
# commande 정보 입력
# [방향, 이동 거리]
command = [list(map(int, input().split())) for _ in range(M)]
# command 방향
# ←, ↖, ↑, ↗, →, ↘, ↓, ↙
dx = [ 0, 0, -1, -1, -1, 0, 1, 1, 1 ]
dy = [ 0, -1, -1, 0, 1, 1, 1, 0, -1 ]
# 물복사를 위한 대각선 방향
# ↖, ↙, ↗, ↘
dx_diagonal = [ -1, 1, -1, 1 ]
dy_diagonal = [ -1, -1, 1, 1 ]
# 0. 초기 구름 좌표 0~N-1로 사용
clouds = [[N-1, 0], [N-1, 1], [N-2, 0],[N-2, 1]]
# M개의 command 실행
for di, si in command:
# 1. 구름 이동
clouds = move_cloud(grid, clouds, di, si)
# 2. 구름이 있는 칸 물량 증가
# 사라질 구름 표시
disappear = [[False for _ in range(N)] for __ in range(N)]
for x, y in clouds:
# 구름이 있는 칸 물량 1 증가
grid[x][y] += 1
# 사라져야함으로 사라질 구름 표시
# 물복사 버그를 진행해야 하기때문에 지금 바로 없애면 안됨
disappear[x][y] = True
# 3. 물복사 버그 시전
Bug(clouds, grid)
# 4. 이전 구름 정보 제거, 새로운 구름 생성
clouds = []
for x in range(N):
for y in range(N):
# 물양이 2이고, 구름이 사라진 곳이 아니면 구름 생성
if grid[x][y] >= 2 and not disappear[x][y]:
clouds.append([x, y])
grid[x][y] -= 2
# 다음 커맨드로 진행
# 5. 끝난 후 물의 양 계산
answer = 0
for x in range(N):
for y in range(N):
answer += grid[x][y]
print(answer)
4. 결과
C++로 예전에 풀엇던 알고리즘 문제를 python으로 복습해보았다. 실행시간은 느리지만 python이 간편하게 코드를 작성할 수 있도록 해주는 것 같다.